Falls sich jemand mittlerweile fragt, wie man automatisiert Nicknamen registrieren kann, hier eine Erläuterung:
Beim Registrierungsprozess werden erst die Daten des Nutzers abgefragt, was ausschließlich Clientseitig geschieht. Bei der Registrierung werden zwei POST-Request's durchgeführt: Einmal das checking, nach der Nicknameneingabe (um zu prüfen, ob der Nick überhaupt registriert werden darf) und einmal das submitting (wo die Daten final an Knuddels gesendet werden und der Nickname registriert wird).
Der Referer ist Standardmäßig https://www.knuddels.de/ und der User-Agent sollte dementsprechend auch einem originalen Webbrowser ähneln.
In den Cookies werden folgende Informationen gespeichert, die wichtigsten sind markiert, ggf. sollte man hier auch das Analyrics random erschleichen, ich weiß nämlich nicht, ob man hierbei ggf. geflaggt wird nach dem Thema "Könnte sich um einen Bot handeln, da keine Analytics-Informationen vorhanden sind":
Bei KnLL handelt es sich vermutlich um den Clienten. "html" bzw. "android" oder "standalone" sollten hier die Werte sein. Vielleicht kann dies ja jemand herausfinden.
Knuddels arbeitet bei der Registrierung mit protobuf, was dann noch base64 ausgeliefert wird. Beide Requests (ob checking oder submitting) enthalten Daten in base64, bereitgestellt durch protobuf. checking
Das checking enthält die Daten des ersten Überprüfungsprozesses. Hier wird von React die jeweilige Route angegeben (/registration.RegistrationRequest)
RAW
0A = field 1, type String
FA-01 = length 250
payload = 0A-21-2F-72-65-67-69-73-74-72-61-74-69-6F-6E-2E-52-65-67-69-73-74-72-61-74-69-6F-6E-52-65-71-75-65-73-74-12-D4-01-08-07-12-0D-52-65-67-54-65-73-74-69-6E-67-31-32-33-18-1C-20-01-42-04-31-32-33-34-48-07-52-00-72-B2-01-53-74-69-6D-6D-73-74-20-64-75-20-64-65-6E-20-41-47-42-20-28-68-74-74-70-73-3A-2F-2F-77-77-77-2E-6B-6E-75-64-64-65-6C-73-2E-64-65-2F-61-67-62-29-20-75-6E-64-20-64-65-72-20-56-65-72-61-72-62-65-69-74-75-6E-67-20-64-65-69-6E-65-72-20-44-61-74-65-6E-20-69-6D-20-52-61-68-6D-65-6E-20-75-6E-73-65-72-65-72-20-44-61-74-65-6E-73-63-68-75-74-7A-65-72-6B-6C-C3-A4-72-75-6E-67-20-28-68-74-74-70-73-3A-2F-2F-77-77-77-2E-6B-6E-75-64-64-65-6C-73-2E-64-65-2F-6C-65-67-61-6C-2F-70-72-69-76-61-63-79-2D-70-6F-6C-69-63-79-2E-68-74-6D-6C-29-20-7A-75-3F
UTF8: !/registration.RegistrationRequest�
RegTesting123 B1234HR�r�Stimmst du den AGB (https://www.knuddels.de/agb) und der Verarbeitung deiner Daten im Rahmen unserer Datenschutzerklärung (https://www.knuddels.de/legal/privacy-policy.html) zu?
0A = field 1, type String
21 = length 33
payload = 2F-72-65-67-69-73-74-72-61-74-69-6F-6E-2E-52-65-67-69-73-74-72-61-74-69-6F-6E-52-65-71-75-65-73-74
UTF8: /registration.RegistrationRequest (Route)
12 = field 2, type String
D4-01 = length 212
payload = 08-07-12-0D-52-65-67-54-65-73-74-69-6E-67-31-32-33-18-1C-20-01-42-04-31-32-33-34-48-07-52-00-72-B2-01-53-74-69-6D-6D-73-74-20-64-75-20-64-65-6E-20-41-47-42-20-28-68-74-74-70-73-3A-2F-2F-77-77-77-2E-6B-6E-75-64-64-65-6C-73-2E-64-65-2F-61-67-62-29-20-75-6E-64-20-64-65-72-20-56-65-72-61-72-62-65-69-74-75-6E-67-20-64-65-69-6E-65-72-20-44-61-74-65-6E-20-69-6D-20-52-61-68-6D-65-6E-20-75-6E-73-65-72-65-72-20-44-61-74-65-6E-73-63-68-75-74-7A-65-72-6B-6C-C3-A4-72-75-6E-67-20-28-68-74-74-70-73-3A-2F-2F-77-77-77-2E-6B-6E-75-64-64-65-6C-73-2E-64-65-2F-6C-65-67-61-6C-2F-70-72-69-76-61-63-79-2D-70-6F-6C-69-63-79-2E-68-74-6D-6C-29-20-7A-75-3F
UTF8:
RegTesting123 B1234HR�r�Stimmst du den AGB (https://www.knuddels.de/agb) und der Verarbeitung deiner Daten im Rahmen unserer Datenschutzerklärung (https://www.knuddels.de/legal/privacy-policy.html) zu?
08 = field 1, type Variant
07 = 7 (raw) or -4 (zigzag)
12 = field 2, type String
0D = length 13
payload = 52-65-67-54-65-73-74-69-6E-67-31-32-33
UTF8: RegTesting123 (Nickname)
18 = field 3, type Variant
1C = 28 (raw) or 14 (zigzag)
20 = field 4, type Variant
01 = 1 (raw) or -1 (zigzag)
42 = field 8, type String
04 = length 4
payload = 31-32-33-34
UTF8: 1234 (Passwort)
48 = field 9, type Variant
07 = 7 (raw) or -4 (zigzag)
52 = field 10, type String
00 = length 0
(empty payload)
72 = field 14, type String
B2-01 = length 178
payload = 53-74-69-6D-6D-73-74-20-64-75-20-64-65-6E-20-41-47-42-20-28-68-74-74-70-73-3A-2F-2F-77-77-77-2E-6B-6E-75-64-64-65-6C-73-2E-64-65-2F-61-67-62-29-20-75-6E-64-20-64-65-72-20-56-65-72-61-72-62-65-69-74-75-6E-67-20-64-65-69-6E-65-72-20-44-61-74-65-6E-20-69-6D-20-52-61-68-6D-65-6E-20-75-6E-73-65-72-65-72-20-44-61-74-65-6E-73-63-68-75-74-7A-65-72-6B-6C-C3-A4-72-75-6E-67-20-28-68-74-74-70-73-3A-2F-2F-77-77-77-2E-6B-6E-75-64-64-65-6C-73-2E-64-65-2F-6C-65-67-61-6C-2F-70-72-69-76-61-63-79-2D-70-6F-6C-69-63-79-2E-68-74-6D-6C-29-20-7A-75-3F
UTF8: Stimmst du den AGB (https://www.knuddels.de/agb) und der Verarbeitung deiner Daten im Rahmen unserer Datenschutzerklärung (https://www.knuddels.de/legal/privacy-policy.html) zu?
Die Daten aus dem Client werden wie folgt definiert:
Es gibt hier zwei Objektfunktionen, die dann die Daten hält und en- bzw. decoded:
t.RegistrationRequest = function() {
function t(t) {
if (this.uids = [],
this.flags = [],
t)
for (var e = Object.keys(t), n = 0; n < e.length; ++n)
null != t[e[n]] && (this[e[n]] = t[e[n]])
}
return t.prototype.type = 0,
t.prototype.nick = "",
t.prototype.age = 0,
t.prototype.gender = 0,
t.prototype.email = "",
t.prototype.cap = "",
t.prototype.capQuest = "",
t.prototype.password = "",
t.prototype.category = 0,
t.prototype.platform = "",
t.prototype.uids = a.emptyArray,
t.prototype.flags = a.emptyArray,
t.prototype.interstitialCandidate = !1,
t.prototype.dsgvoQuestion = "",
t.encode = function(t, e) {
if (e || (e = i.create()),
null != t.type && t.hasOwnProperty("type") && e.uint32(8).int32(t.type),
null != t.nick && t.hasOwnProperty("nick") && e.uint32(18).string(t.nick),
null != t.age && t.hasOwnProperty("age") && e.uint32(24).int32(t.age),
null != t.gender && t.hasOwnProperty("gender") && e.uint32(32).int32(t.gender),
null != t.email && t.hasOwnProperty("email") && e.uint32(42).string(t.email),
null != t.cap && t.hasOwnProperty("cap") && e.uint32(50).string(t.cap),
null != t.capQuest && t.hasOwnProperty("capQuest") && e.uint32(58).string(t.capQuest),
null != t.password && t.hasOwnProperty("password") && e.uint32(66).string(t.password),
null != t.category && t.hasOwnProperty("category") && e.uint32(72).int32(t.category),
null != t.platform && t.hasOwnProperty("platform") && e.uint32(82).string(t.platform),
null != t.uids && t.uids.length)
for (var n = 0; n < t.uids.length; ++n)
e.uint32(90).string(t.uids[n]);
if (null != t.flags && t.flags.length)
for (var n = 0; n < t.flags.length; ++n)
e.uint32(98).string(t.flags[n]);
return null != t.interstitialCandidate && t.hasOwnProperty("interstitialCandidate") && e.uint32(104).bool(t.interstitialCandidate),
null != t.dsgvoQuestion && t.hasOwnProperty("dsgvoQuestion") && e.uint32(114).string(t.dsgvoQuestion),
e
}
,
t.encodeDelimited = function(t, e) {
return this.encode(t, e).ldelim()
}
,
t.decode = function(t, e) {
t instanceof o || (t = o.create(t));
for (var n = void 0 === e ? t.len : t.pos + e, r = new s.registration.RegistrationRequest; t.pos < n; ) {
var i = t.uint32();
switch (i >>> 3) {
case 1:
r.type = t.int32();
break;
case 2:
r.nick = t.string();
break;
case 3:
r.age = t.int32();
break;
case 4:
r.gender = t.int32();
break;
case 5:
r.email = t.string();
break;
case 6:
r.cap = t.string();
break;
case 7:
r.capQuest = t.string();
break;
case 8:
r.password = t.string();
break;
case 9:
r.category = t.int32();
break;
case 10:
r.platform = t.string();
break;
case 11:
r.uids && r.uids.length || (r.uids = []),
r.uids.push(t.string());
break;
case 12:
r.flags && r.flags.length || (r.flags = []),
r.flags.push(t.string());
break;
case 13:
r.interstitialCandidate = t.bool();
break;
case 14:
r.dsgvoQuestion = t.string();
break;
default:
t.skipType(7 & i)
}
}
return r
}
,
t.decodeDelimited = function(t) {
return t instanceof o || (t = new o(t)),
this.decode(t, t.uint32())
}
,
t.verify = function(t) {
if ("object" !== typeof t || null === t)
return "object expected";
if (null != t.type && t.hasOwnProperty("type"))
switch (t.type) {
default:
return "type: enum value expected";
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
}
if (null != t.nick && t.hasOwnProperty("nick") && !a.isString(t.nick))
return "nick: string expected";
if (null != t.age && t.hasOwnProperty("age") && !a.isInteger(t.age))
return "age: integer expected";
if (null != t.gender && t.hasOwnProperty("gender"))
switch (t.gender) {
default:
return "gender: enum value expected";
case 0:
case 1:
case 2:
}
if (null != t.email && t.hasOwnProperty("email") && !a.isString(t.email))
return "email: string expected";
if (null != t.cap && t.hasOwnProperty("cap") && !a.isString(t.cap))
return "cap: string expected";
if (null != t.capQuest && t.hasOwnProperty("capQuest") && !a.isString(t.capQuest))
return "capQuest: string expected";
if (null != t.password && t.hasOwnProperty("password") && !a.isString(t.password))
return "password: string expected";
if (null != t.category && t.hasOwnProperty("category") && !a.isInteger(t.category))
return "category: integer expected";
if (null != t.platform && t.hasOwnProperty("platform") && !a.isString(t.platform))
return "platform: string expected";
if (null != t.uids && t.hasOwnProperty("uids")) {
if (!Array.isArray(t.uids))
return "uids: array expected";
for (var e = 0; e < t.uids.length; ++e)
if (!a.isString(t.uids[e]))
return "uids: string[] expected"
}
if (null != t.flags && t.hasOwnProperty("flags")) {
if (!Array.isArray(t.flags))
return "flags: array expected";
for (var e = 0; e < t.flags.length; ++e)
if (!a.isString(t.flags[e]))
return "flags: string[] expected"
}
return null != t.interstitialCandidate && t.hasOwnProperty("interstitialCandidate") && "boolean" !== typeof t.interstitialCandidate ? "interstitialCandidate: boolean expected" : null != t.dsgvoQuestion && t.hasOwnProperty("dsgvoQuestion") && !a.isString(t.dsgvoQuestion) ? "dsgvoQuestion: string expected" : null
}
,
t
}(),
sowie
t.RegistrationResponse = function() {
function t(t) {
if (this.oks = [],
this.errors = [],
t)
for (var e = Object.keys(t), n = 0; n < e.length; ++n)
null != t[e[n]] && (this[e[n]] = t[e[n]])
}
return t.prototype.oks = a.emptyArray,
t.prototype.errors = a.emptyArray,
t.prototype.success = null,
t.prototype.nickContext = "",
t.encode = function(t, e) {
if (e || (e = i.create()),
null != t.oks && t.oks.length)
for (var n = 0; n < t.oks.length; ++n)
e.uint32(10).string(t.oks[n]);
if (null != t.errors && t.errors.length)
for (var n = 0; n < t.errors.length; ++n)
s.registration.Error.encode(t.errors[n], e.uint32(18).fork()).ldelim();
return null != t.success && t.hasOwnProperty("success") && s.registration.Success.encode(t.success, e.uint32(26).fork()).ldelim(),
null != t.nickContext && t.hasOwnProperty("nickContext") && e.uint32(34).string(t.nickContext),
e
}
,
t.encodeDelimited = function(t, e) {
return this.encode(t, e).ldelim()
}
,
t.decode = function(t, e) {
t instanceof o || (t = o.create(t));
for (var n = void 0 === e ? t.len : t.pos + e, r = new s.registration.RegistrationResponse; t.pos < n; ) {
var i = t.uint32();
switch (i >>> 3) {
case 1:
r.oks && r.oks.length || (r.oks = []),
r.oks.push(t.string());
break;
case 2:
r.errors && r.errors.length || (r.errors = []),
r.errors.push(s.registration.Error.decode(t, t.uint32()));
break;
case 3:
r.success = s.registration.Success.decode(t, t.uint32());
break;
case 4:
r.nickContext = t.string();
break;
default:
t.skipType(7 & i)
}
}
return r
}
,
t.decodeDelimited = function(t) {
return t instanceof o || (t = new o(t)),
this.decode(t, t.uint32())
}
,
t.verify = function(t) {
if ("object" !== typeof t || null === t)
return "object expected";
if (null != t.oks && t.hasOwnProperty("oks")) {
if (!Array.isArray(t.oks))
return "oks: array expected";
for (var e = 0; e < t.oks.length; ++e)
if (!a.isString(t.oks[e]))
return "oks: string[] expected"
}
if (null != t.errors && t.hasOwnProperty("errors")) {
if (!Array.isArray(t.errors))
return "errors: array expected";
for (var e = 0; e < t.errors.length; ++e) {
var n = s.registration.Error.verify(t.errors[e]);
if (n)
return "errors." + n
}
}
if (null != t.success && t.hasOwnProperty("success")) {
var n = s.registration.Success.verify(t.success);
if (n)
return "success." + n
}
return null != t.nickContext && t.hasOwnProperty("nickContext") && !a.isString(t.nickContext) ? "nickContext: string expected" : null
}
,
t
}(),
t
}(),
Und das Networking wird über folgende Methode initiiert:
return e = Object(o.getSelectedServer)(Object(s.c)().getState()),
n = o.Servers[e].ORIGIN + "/registration/registration_submit.html",
r = new l(t),
[4, o.NetworkManager.protobufFetch("post", r, n)];
Source fon protobufFetch:
"2pj3": function(t, e, n) {
"use strict";
function r(t, e, n, r, o) {
var s, u = "get" === t;
s = r ? r.encode(e).finish() : Object(i.c)(e);
var c = Object(a.b)(s);
u && (n = "/" === n.slice(-1) ? n.slice(0, -1) : n);
var l = {
Accept: "application/x-protobuf+base64",
"Content-Type": "application/x-protobuf+base64; charset=UTF-8"
};
void 0 !== o && (l.Token = o);
var f = {
method: t,
headers: u ? void 0 : l,
body: u ? void 0 : c
};
return fetch(n, f)
}
function o(t) {
return s(this, void 0, void 0, function() {
var e;
return u(this, function(n) {
switch (n.label) {
case 0:
return e = a.a,
[4, t.text()];
case 1:
return [2, e.apply(void 0, [n.sent()])]
}
})
})
}
e.a = r,
e.b = o;
var i = n("9uC1")
, a = n("zcVu")
, s = this && this.__awaiter || function(t, e, n, r) {
return new (n || (n = Promise))(function(o, i) {
function a(t) {
try {
u(r.next(t))
} catch (t) {
i(t)
}
}
function s(t) {
try {
u(r.throw(t))
} catch (t) {
i(t)
}
}
function u(t) {
t.done ? o(t.value) : new n(function(e) {
e(t.value)
}
).then(a, s)
}
u((r = r.apply(t, e || [])).next())
}
)
}
, u = this && this.__generator || function(t, e) {
function n(t) {
return function(e) {
return r([t, e])
}
}
function r(n) {
if (o)
throw new TypeError("Generator is already executing.");
for (; u; )
try {
if (o = 1,
i && (a = 2 & n[0] ? i.return : n[0] ? i.throw || ((a = i.return) && a.call(i),
0) : i.next) && !(a = a.call(i, n[1])).done)
return a;
switch (i = 0,
a && (n = [2 & n[0], a.value]),
n[0]) {
case 0:
case 1:
a = n;
break;
case 4:
return u.label++,
{
value: n[1],
done: !1
};
case 5:
u.label++,
i = n[1],
n = [0];
continue;
case 7:
n = u.ops.pop(),
u.trys.pop();
continue;
default:
if (a = u.trys,
!(a = a.length > 0 && a[a.length - 1]) && (6 === n[0] || 2 === n[0])) {
u = 0;
continue
}
if (3 === n[0] && (!a || n[1] > a[0] && n[1] < a[3])) {
u.label = n[1];
break
}
if (6 === n[0] && u.label < a[1]) {
u.label = a[1],
a = n;
break
}
if (a && u.label < a[2]) {
u.label = a[2],
u.ops.push(n);
break
}
a[2] && u.ops.pop(),
u.trys.pop();
continue
}
n = e.call(t, u)
} catch (t) {
n = [6, t],
i = 0
} finally {
o = a = 0
}
if (5 & n[0])
throw n[1];
return {
value: n[0] ? n[1] : void 0,
done: !0
}
}
var o, i, a, s, u = {
label: 0,
sent: function() {
if (1 & a[0])
throw a[1];
return a[1]
},
trys: [],
ops: []
};
return s = {
next: n(0),
throw: n(1),
return: n(2)
},
"function" === typeof Symbol && (s[Symbol.iterator] = function() {
return this
}
),
s
}
},
Ich werde bei Gelegenheit ein eigenes Reg-Tool erstellen, was auch diverse Features besitzt (Proxy usage, human emulation,..) und das dann zur Verfügung stellen. Ansonsten wünsche ich euch Happy Coding!
13.01.2019, 07:03
Darkness
AW: Registrierung
Sind diese Weiberbotnicks mit dem "Komm auf www.blabla.de fick mich" foto von dir?
13.01.2019, 12:04
Bubble Gum
AW: Registrierung
Nein ^^
Ich feier die nur, weil Knuddels da seit Wochen nichts macht :D
13.01.2019, 16:12
Darkness
AW: Registrierung
Schade, kriege nämlich nur noch diese Nicks als Schützling, kp woran das liegt, und da kriegt man natürlich nie eine Antwort, falls diese Nicks jemandem vom hier sind, meld dich mal ^^ würde die Nicks abkaufen die ich als Schützi angeboten bekomme :D
13.01.2019, 19:57
Bubble Gum
AW: Registrierung
Ich glaube kaum, dass automatisierte Dinge dir ernsthaft antworten werden, geschweige derjenige sich meldet, der dafür Verantwortlich ist :D
Laut Knuddels wäre dies angeblich nicht automatisiert bzw. wären keine Bots; Finde ich sehr Lustig, dass die dies wohl nicht checken. Scheinen wohl so Dumm zu sein.
Derjenige, der das war, muss dies schon professionell gemacht haben. Es scheint niemand aus dem bekannten Underground zu sein, schon alleine weil protobuf etwas komplexer und von keinem Script-Kiddie angewandt werden kann. Kann mich natürlich auch täuschen und derjenige geht einfach hin und ändert den Inhalt vom base64 encoded String um einfach die Anfrage zu ändern..
- - - Aktualisiert - - -
Hab jetzt mal mehrere Nicks registriert. In der Theorie kann man einfach die Bytes ändern.
Die letzte Sektion beim checking ist zum beispiel <inetgerLengthAsByte><NicknameString>
15.01.2019, 20:39
Bubble Gum
Liste der Anhänge anzeigen (Anzahl: 2)
AW: Registrierung
Ich bin gerade dabei, protobuf aus technischer Sicht zu verstehen, insbesondere, wie der bytecode aufgebaut ist.
Das Problem ist derzeit das compilen und auch definieren für protobuf einfach zu umständlich ist, deshalb werde ich wohl kurzerhand einen eigenen Encoder/Decoder basteln. Die Datenstruktur ist wie bei JSON relativ easy, wenn man das mal verstanden hat. Protobuf besteht zum größtenteils aus Varints, was das vereinfacht.
Da ich aber momentan im Krankenhaus festsitze, kann ich leider nicht so sehr herumexperimentieren, bisher habe ich mir dazu aber schonmal einige Grundgedanken gemacht: Anhang 9337Anhang 9338
Mit der Basis sollte man dann im späteren Verlaufe die Datentypen herausfinden (Siehe Doxs/RFC-Draft) und die jeweiligen Sachen en- bzw. decoden.
16.01.2019, 23:38
Mephistochen
AW: Registrierung
Gute Besserung!
24.01.2019, 12:50
Bubble Gum
AW: Registrierung
Sooo, ich bin schon etwas weiter gekommen. Ich hab jetzt erst einmal direkt mit protobuf herumexperimentiert und habe die benötigte proto-File rekonstruiert!
packet {
action: "/registration.RegistrationRequest"
data {
type: FULL_REG_V1
nickname: "RegTesting123"
age: 28
gender: MALE
password: "1234"
category: 7
dsgvoQuestion: "Stimmst du den AGB ([url]https://www.knuddels.de/agb[/url]) und der Verarbeitung deiner Daten im Rahmen unserer Datenschutzerkl\303\244rung ([url]https://www.knuddels.de/legal/privacy-policy.html[/url]) zu?"
}
}
Mit diesen strukturierten Daten kann man auf jeden Fall gut arbeiten. Falls jemand wissen möchte, wie ich die Feld-Typen herausgefunden habe: Schaut in den webpack-Rotz der Startseite, wo die Registrierung erreichbar ist. Beim beautify sieht man die Daten recht zügig.
Und hier nun die Proto-File, mit dieser kannst du dann selbst "Packet-Daten" für die Registrierung zusammenschustern:
var Bundle = root.lookupType('Bundle');
var RequestType = root.lookup('').RequestType;
var Gender = root.lookup('').Gender;
var Payload = {
action: '/registration.RegistrationRequest',
data: {
type: RequestType.FULL_REG_V1,
nickname: nickname,
age: age,
gender: LookUpGender(Gender, gender),
password: password,
category: category,
dsgvoQuestion: 'Stimmst du den AGB ([url]https://www.knuddels.de/agb[/url]) und der Verarbeitung deiner Daten im Rahmen unserer Datenschutzerkl\303\244rung ([url]https://www.knuddels.de/legal/privacy-policy.html[/url]) zu?'
}
};
var exception = Bundle.verify(Payload);
if(exception) {
throw Error(exception);
}
var message = Bundle.create(Payload);
var buffer = Bundle.encode(message).finish();
callback(buffer);
});
}
Check('Holgi', function onSuccess(buffer) {
console.log(buffer); // Den Buffer nun zu base64 encoden und via POST-Request an Knuddels senden.
});
/*
Gender: Male, MALE, maLe, 1
Female, FEMALE, fEmAlE, 2
*/
Submit('Holgi', 'DatPasswort', 'Male', 32, 7, function onSuccess(buffer) {
console.log(buffer); // Den Buffer nun zu base64 encoden und via POST-Request an Knuddels senden.
});
Mittlerweile kann man das ganze in verschiedenen Szenarien verwenden:
Direkt über CMD / CLI / Shell
Als npm Package in einem eigenen Node.js Projekt
Als CMD / CLI / Shell executement mit den jeweiligen Argumenten
Wenn das ganze später fertig ist, werde ich das auf npm bereitstellen, damit man das nur noch über npm installieren braucht. Bisher kann man das ganze Repo aber clonen und einfach mit require einbinden.