die heldin script add

This commit is contained in:
2026-02-24 07:04:01 +01:00
parent a1b35fecce
commit 420391f48a
1847 changed files with 597777 additions and 0 deletions

841
node_modules/openmagicline/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,841 @@
import { AxiosInstance } from 'axios';
import debug from 'debug';
type CustomerID = number
type AccessIdentificationID = number
interface SearchOptions {
facility?: unitID
/**
* searchInAddress
* @default false
*/
searchInAddress?: boolean
/**
* searchInBankAccount
* @default false
*/
searchInBankAccount?: boolean
/**
* searchInCardNumber
* @default false
*/
searchInCardNumber?: boolean
/**
* searchInCustomerNumber
* @default true
*/
searchInCustomerNumber?: boolean
/**
* searchInLockerKey
* @default false
*/
searchInLockerKey?: boolean
/**
* searchInName
* @default true
*/
searchInName?: boolean
/**
* searchInPurchasedContingentCode
* @default false
*/
searchInPurchasedContingentCode?: boolean
/**
* showAllFacilities
* @default true
*/
showAllFacilities?: boolean
/**
* showCheckedIn
* @default false
*/
showCheckedIn?: boolean
/**
* showOnlyMembers
* @default false
*/
showOnlyMembers?: boolean
}
type customer_d$1_AccessIdentificationID = AccessIdentificationID;
type customer_d$1_CustomerID = CustomerID;
type customer_d$1_SearchOptions = SearchOptions;
declare namespace customer_d$1 {
export {
customer_d$1_AccessIdentificationID as AccessIdentificationID,
customer_d$1_CustomerID as CustomerID,
customer_d$1_SearchOptions as SearchOptions,
};
}
interface ListOptions {
organizationUnitId?: unitID
offset?: number
/** magiclines webclient sets this to */
maxResults?: number
search?: string
filter?: string
sortedby?: "checkinTime" | "checkinDuration" | "lastname" | "firstname" | "lockerKey"
/** list checkouts instead */
checkouts?: boolean
direction?: "DESCENDING" | "ASCENDING"
}
interface CheckinOptions {
/** not sure what this is for */
customerCardNumber?: null | unknown
/** not sure what this is for */
customerUUID?: string
/** customerID */
fkCustomer: number
/** not sure what this is for */
fkDevice?: null | unknown
/** organizationUnitId */
fkOrganizationUnit?: unitID
lockerKey?: number | string
/** not sure what this is for */
purchasedContingentCode?: null | unknown
/** not sure what this is for */
databaseId?: null | unknown
/** not sure what this is for, was 0 for me */
optlock?: number
/** organizationUnitId */
requiredOrganizationUnitId?: unitID
}
interface CheckoutOptions {
optLockRemote?: number
}
interface LockerKeyOptions {
/** not sure what this is for */
databaseId?: null | unknown
/** not sure what this is for */
optlock?: 0 | number
}
type checkin_d$1_CheckinOptions = CheckinOptions;
type checkin_d$1_CheckoutOptions = CheckoutOptions;
type checkin_d$1_ListOptions = ListOptions;
type checkin_d$1_LockerKeyOptions = LockerKeyOptions;
declare namespace checkin_d$1 {
export {
checkin_d$1_CheckinOptions as CheckinOptions,
checkin_d$1_CheckoutOptions as CheckoutOptions,
checkin_d$1_ListOptions as ListOptions,
checkin_d$1_LockerKeyOptions as LockerKeyOptions,
};
}
interface ProductOptions {
organizationUnitId?: unitID
customerId?: number
}
type sales_d$1_ProductOptions = ProductOptions;
declare namespace sales_d$1 {
export {
sales_d$1_ProductOptions as ProductOptions,
};
}
interface Config {
/**
* the gym's url prefix
*
* when you access your dashboard via https://example.web.magicline.com
* your url prefix will be `example`
*/
gym: string
/**
* login username, making a dedicated account for API access is recommended.
*/
username?: string
/**
* login password, making a dedicated account for API access is recommended.
*/
password?: string
// unitID?: number
}
type index_d$1_Config = Config;
declare namespace index_d$1 {
export {
checkin_d$1 as Checkin,
index_d$1_Config as Config,
customer_d$1 as Customer,
sales_d$1 as Sales,
};
}
interface Benefit {
name: string
available: number | null
total: number | null
hasUsages: boolean
colorHex: string
unit: string
inclusiveContingentId: null
purchasedContingentId: number | null
trialContingentId: null
freeUsage: boolean
benefitId: number
expiryDate: null | string
createdDate: null | string
durationText: null
flat: boolean
}
interface Base {
databaseId: CustomerID
}
interface CheckinCondition {
type: string
messageKey: string
args: string[]
}
interface SearchedCustomer extends Base {
firstname: string
lastname: string
gender: number
dateOfBirth: Date | null
lastCheckIn: Date | null
facilityName: string
customerNumber: string
cardNumber: null
lockerKey: null | string
customerStatus: number
purchasedContingentCode: null
purchasedContingentType: null
imageUrl: null
checkedIn: boolean
street: null | string
houseNumber: null | string
zip: null | string
city: null | string
}
interface AccessIdentification {
databaseId: AccessIdentificationID
optlock: number
fkOrganizationUnit: number
createdDate: Date
type: string
uid: string
stompDestination: string
}
interface Contract {
databaseId: number
optlock: number
rateId: number
rateName: string
publicRateName: string
compunknownId: null
currencyUnit: string
startDate: string
startDateOfUse: null | string
startDateCurrentTerm: null | string
preuseType: number
endDate: string
term: number
extensionType: string
termUnit: number
termExtension: number
termExtensionUnit: number
extensionCancelationPeriod: null
trialPeriod: null
paymentChoice: null
cancelationPeriod: number
cancelationPeriodUnit: number
nextCancelationDate: string
active: boolean
activeAgeBasedAdjustmentDto: null
isCanceled: boolean | null
isReversed: boolean
imported: boolean
hasBonusPeriods: boolean
hasContractIdlePeriods: boolean
contractOutlier: unknown[]
currentPrice: number
currentPriceWithDate: CurrentPriceWithDate
nextPriceWithDate: null
contractCancelation: ContractCancelation | null
paymentDay: number | null
contractPaymentFrequency: ContractPaymentFrequency
moduleContracts: null
flatFeeContracts: FlatFeeContract[] | null
divergentAffiliate: null
contractRestMaturityDto: null
cancelationStrategy: string
subsequentRateName: null
subsequentRateDetailTerm: null
subsequentRateDetailTermUnit: null
subsequentRateDetailPaymentFrequencyTerm: null
subsequentRateDetailPaymentFrequencyTermUnit: null
subsequentRateDetailPaymentFrequencyType: null
subsequentRateDetailPaymentFrequencyPrice: null
subsequentContractId: null
subsequentContractHasSubsequentContract: boolean
subsequentContractName: null
signatureStatus: string
campaignId: null
salesSource: string
notes: null
bonusPeriods: unknown[]
contractIdlePeriods: unknown[]
vouchers: unknown[]
paymentRunGroupSimpleDto: null
}
interface ContractCancelation {
databaseId: number
optlock: number
fkCancelationReason: number
cancelationReasonName: string
cancelationReceiptDate: string
cancelationDate: string
extraordinaryCancelation: boolean
createSystemMessage: boolean
customerMessageId: null
cancelationFee: null
ignoreImportedChargesOnChargeCalendarCalculation: boolean
cancelationByStudio: boolean
}
interface ContractPaymentFrequency {
databaseId: number
optlock: number
type: string
value: number | null
unit: number | null
price: number
money: Money
paidTimePeriodCalculationType: null | string
firstBookingDelay: FirstBookingDelay | null
recurring: null
extensionValue: null
extensionUnit: null
extensionPrice: null
dueDateOffset: null
applyFirstEncashmentOnFirstRegularCharge: boolean
monthDayWithPrices: unknown[]
termWithPrices: unknown[]
monthDays: unknown[]
ageBasedAdjustmentDtos: unknown[]
firstEncashment: string
dynamicAdjustmentRules: unknown[]
}
interface FirstBookingDelay {
term: number
termUnit: number
}
interface Money {
amount: number
currencyCode: string
}
interface CurrentPriceWithDate {
date: string
price: number
}
interface FlatFeeContract {
databaseId: number
optlock: number
currencyUnit: string
startDate: string
startDateOfUse: null
endDate: string
imported: boolean
paymentDay: null
contractCancelation: null
contractPaymentFrequency: ContractPaymentFrequency
contractOutlier: null
contractRestMaturityDto: null
retroactive: boolean
termInformation: null
paymentChoice: null
paymentRunGroupSimpleDto: null
flatFeeRateId: number
flatFeeRateName: string
flatFeeRatePublicName: string
flatFeeRateDetailPaymentFrequencyId: number
reversed: boolean
canceled: boolean
}
interface DetailedBalance {
databaseId: number
optlock: null
consumptionCreditBalance: number
debtClaimBalance: number
debtClaimBalanceWithoutLaterSale: number
laterSaleBalance: number
paymentBalance: number
transferBalance: number
totalWithoutConsumptionCredit: number
}
type customer_d_AccessIdentification = AccessIdentification;
type customer_d_Base = Base;
type customer_d_Benefit = Benefit;
type customer_d_CheckinCondition = CheckinCondition;
type customer_d_Contract = Contract;
type customer_d_ContractCancelation = ContractCancelation;
type customer_d_ContractPaymentFrequency = ContractPaymentFrequency;
type customer_d_CurrentPriceWithDate = CurrentPriceWithDate;
type customer_d_DetailedBalance = DetailedBalance;
type customer_d_FirstBookingDelay = FirstBookingDelay;
type customer_d_FlatFeeContract = FlatFeeContract;
type customer_d_Money = Money;
type customer_d_SearchedCustomer = SearchedCustomer;
declare namespace customer_d {
export {
customer_d_AccessIdentification as AccessIdentification,
customer_d_Base as Base,
customer_d_Benefit as Benefit,
customer_d_CheckinCondition as CheckinCondition,
customer_d_Contract as Contract,
customer_d_ContractCancelation as ContractCancelation,
customer_d_ContractPaymentFrequency as ContractPaymentFrequency,
customer_d_CurrentPriceWithDate as CurrentPriceWithDate,
customer_d_DetailedBalance as DetailedBalance,
customer_d_FirstBookingDelay as FirstBookingDelay,
customer_d_FlatFeeContract as FlatFeeContract,
customer_d_Money as Money,
customer_d_SearchedCustomer as SearchedCustomer,
};
}
interface LockerKeyResponse {
checkinId: number
lockerKey: string
}
interface CheckinResponse {
/** this is the checkinID **not** the customerID, that is `fkCustomer` */
databaseId: number
optlock: number
fkOrganizationUnit: number
/** this is the customerID */
fkCustomer: number
fkEmployee: null
fkDevice: number | null
firstname: string
lastname: string
cardNumber: null
customerNumber: string
employeeNumber: null
dateOfBirth: string
gender: number
imageUrl: null
studioName: null
lockerKey: null | string
checkinTime: string
checkoutTime: null | string
stompDestination: string
}
interface CheckinList {
checkins: Checkin$1[]
summary: { [key: string]: number }
}
interface Checkin$1 {
databaseId: number
optlock: null
customerId: number
customerOrganizationUnitId: number
customerFacilityName: string
lockerKey: null | string
checkinTime: string
previousCheckinTime: null | string
lastCheckoutTime: string
firstname: string
lastname: string
customerNumber: string
gender: number
dateOfBirth: null | string
imageUrl: ImageURL$1 | null
customerCodes: CustomerCode[]
contractNames: string[]
moduleNames: string[]
badges: Badge[]
isAnonymized: boolean
}
interface Badge {
badge: string
label: string
}
interface CustomerCode {
databaseId: number
optlock: number
facilityInfo: null
name: string
description: null | string
colorHex: string
active: boolean
}
interface ImageURL$1 {
url: string
rewrite: boolean
}
type checkin_d_Badge = Badge;
type checkin_d_CheckinList = CheckinList;
type checkin_d_CheckinResponse = CheckinResponse;
type checkin_d_CustomerCode = CustomerCode;
type checkin_d_LockerKeyResponse = LockerKeyResponse;
declare namespace checkin_d {
export {
checkin_d_Badge as Badge,
Checkin$1 as Checkin,
checkin_d_CheckinList as CheckinList,
checkin_d_CheckinResponse as CheckinResponse,
checkin_d_CustomerCode as CustomerCode,
ImageURL$1 as ImageURL,
checkin_d_LockerKeyResponse as LockerKeyResponse,
};
}
interface ProductOverview {
fkOrganizationUnit: null
customer: null
classOfGoodsList: ClassOfGoodsList[]
}
interface ClassOfGoodsList {
databaseId: number
name: string
materialClassOfGoods: boolean
voucher: boolean
productList: ProductList[]
}
interface ProductList {
databaseId: number | null
name: string
regularTaxRate: TaxRate
toGoTaxRate: TaxRate | null
productVariantList: ProductVariantList[]
}
interface ProductVariantList {
databaseId: number
name: string
itemNumber: string
currencyUnit: string
price: number
additionalInfo: null | string
stock: number | null
ean: null | string
}
interface TaxRate {
name: string
percent: number
}
type sales_d_ClassOfGoodsList = ClassOfGoodsList;
type sales_d_ProductList = ProductList;
type sales_d_ProductOverview = ProductOverview;
type sales_d_ProductVariantList = ProductVariantList;
type sales_d_TaxRate = TaxRate;
declare namespace sales_d {
export {
sales_d_ClassOfGoodsList as ClassOfGoodsList,
sales_d_ProductList as ProductList,
sales_d_ProductOverview as ProductOverview,
sales_d_ProductVariantList as ProductVariantList,
sales_d_TaxRate as TaxRate,
};
}
type CallbackFunction<T> = (data: T) => void
type UnsubscribeFunction = () => void
interface CheckinEvent {
type: string
action: string
timestamp: number
tenantName: string
organizationUnitId: number
customerIds: unknown[]
payload: Payload
}
interface Payload {
databaseId: number
optlock: number
fkOrganizationUnit: number
fkCustomer: number
fkEmployee: null
fkDevice: number
firstname: string
lastname: string
cardNumber: null
customerNumber: string
employeeNumber: null
dateOfBirth: string
gender: number
imageUrl: ImageURL | null
studioName: null
lockerKey: null | string
checkinTime: string
checkoutTime: null | string
stompDestination: string
}
interface ImageURL {
url: string
rewrite: boolean
}
type socket_d_CallbackFunction<T> = CallbackFunction<T>;
type socket_d_CheckinEvent = CheckinEvent;
type socket_d_ImageURL = ImageURL;
type socket_d_Payload = Payload;
type socket_d_UnsubscribeFunction = UnsubscribeFunction;
declare namespace socket_d {
export {
socket_d_CallbackFunction as CallbackFunction,
socket_d_CheckinEvent as CheckinEvent,
socket_d_ImageURL as ImageURL,
socket_d_Payload as Payload,
socket_d_UnsubscribeFunction as UnsubscribeFunction,
};
}
type CurrentLocale = string
type SupportedLocales = string[]
interface Success {
success: "true"
}
interface GenericError {
errorMessage: string
errorCode: string
args: unknown[]
typedArgs: unknown[]
}
type ErrorOrSuccess = Partial<Success> & GenericError[]
interface Permitted {
databaseId: null | number
optlock: null | number
name: string
prefix: null | string
type: number
country: null | string
colorHex: string
currencyCode: string
timeZone: string
currency: {
databaseId: null | number
optlock: null | number
unit: string
}
isTenantWithSingleStudio: boolean
permitted: boolean
listChildren: Permitted[]
permissions: string[]
isStudioLoginPermitted: boolean
}
type Notices = unknown[]
interface AccountInfo {
firstname: string
lastname: string
fullName: string
employeeId: number
imageUrl: {
url: string
rewrite: boolean
}
}
interface App {
app: string
status: string
values: {
id?: number
category: string
tier?: string
payment_provider_id?: string
}
}
type index_d_AccountInfo = AccountInfo;
type index_d_App = App;
type index_d_CurrentLocale = CurrentLocale;
type index_d_ErrorOrSuccess = ErrorOrSuccess;
type index_d_GenericError = GenericError;
type index_d_Notices = Notices;
type index_d_Permitted = Permitted;
type index_d_Success = Success;
type index_d_SupportedLocales = SupportedLocales;
declare namespace index_d {
export {
index_d_AccountInfo as AccountInfo,
index_d_App as App,
checkin_d as Checkin,
index_d_CurrentLocale as CurrentLocale,
customer_d as Customer,
index_d_ErrorOrSuccess as ErrorOrSuccess,
index_d_GenericError as GenericError,
index_d_Notices as Notices,
index_d_Permitted as Permitted,
sales_d as Sales,
socket_d as Socket,
index_d_Success as Success,
index_d_SupportedLocales as SupportedLocales,
};
}
/**
* the "organizationUnitId" used to identify different units for the same gym.
* that's what i think what it is at least.
*
* if it's marked as optional it can be omitted for convenience, in which case it
* falls back to the first listed unit, **but it *should* always be provided**.
*
* mine was 2 by default for some reason, you can find yours using `.permitted()`
*/
type unitID = number
declare class Util {
private axios;
private mgl;
constructor(axios: AxiosInstance, mgl: Openmagicline);
getDefaultUnitID(): Promise<unitID>;
/**
* check if the login token works.
*/
testLogin(): Promise<boolean>;
}
declare class Locale {
private axios;
constructor(axios: AxiosInstance);
currentLocale(): Promise<CurrentLocale>;
supportedLocales(): Promise<SupportedLocales>;
}
declare class Organization {
private axios;
private mgl;
constructor(axios: AxiosInstance, mgl: Openmagicline);
permitted(): Promise<Permitted>;
accountInfo(): Promise<AccountInfo>;
apps(unitID?: unitID): Promise<App[]>;
}
declare class Customer {
private axios;
private mgl;
constructor(axios: AxiosInstance, mgl: Openmagicline);
defaultSearchOptions: Required<SearchOptions>;
search(searchString: string, options?: SearchOptions): Promise<SearchedCustomer[]>;
getCards(customerID: CustomerID): Promise<AccessIdentification[]>;
/**
* get contracts of a customer
* @param customerId customer id
* @param isActive get only active contracts (default: `true`)
*/
contract: (customerId: CustomerID, isActive?: boolean) => Promise<Contract[]>;
checkinConditions: (customerId: number, organizationUnitId?: number) => Promise<CheckinCondition[]>;
benefits: (customerId: CustomerID, active?: boolean | "both") => Promise<Benefit[]>;
detailedBalance: (customerId: CustomerID) => Promise<DetailedBalance>;
}
declare class Checkin {
private axios;
private mgl;
constructor(axios: AxiosInstance, mgl: Openmagicline);
defaultListParams: ListOptions;
/**
* list all checked-in customers
* @param options filter, sort, etc.
*/
list(options?: ListOptions): Promise<CheckinList>;
private defaultCheckinParams;
/**
* check-in a customer
*/
checkin(options: CheckinOptions): Promise<CheckinResponse>;
/**
* check-out a customer
* @param checkinId the ID of the checkin, **not** the customer ID
* @param options optional object containing optLockRemote, not sure what it does
*/
checkout(checkinId: number, options?: CheckoutOptions): Promise<CheckinResponse>;
private defaultLockerKeyParams;
lockerKey(checkinId: number, lockerKey: number | string, options?: LockerKeyOptions): Promise<LockerKeyResponse>;
}
declare class Sales {
private axios;
private mgl;
constructor(axios: AxiosInstance, mgl: Openmagicline);
products(options?: ProductOptions): Promise<ProductOverview>;
}
declare class MagicSocket {
private mgl;
private client;
private log;
isActive: false | Promise<unknown>;
private webSocketFactory;
constructor(mgl: Openmagicline, unitID: unitID);
subscriptions: Record<string, UnsubscribeFunction>;
activate(): Promise<unknown>;
unsubscribeAll(): Promise<void>;
deactivate(): Promise<void>;
private deactivateAutomatically;
private subscribeFactory;
onCheckin: (callback: CallbackFunction<CheckinEvent>) => Promise<UnsubscribeFunction>;
}
/** @deprecated todo: move to util */
declare const _log: debug.Debugger;
declare class Openmagicline {
private config;
protected log: debug.Debugger;
protected axios: AxiosInstance;
baseUrl: string;
cookies?: string[];
customer: Customer;
locale: Locale;
organization: Organization;
/** everything related to the checkin process */
checkin: Checkin;
/** miscellaneous helpers and thingies */
util: Util;
/** everything related to retail sales (magicline calls this disposal in some places) */
sales: Sales;
/** reference to this.sales */
disposal: Sales;
/** event handler for magiclines websockets */
socket: (unitID: unitID) => MagicSocket;
constructor(config: Config, axios?: AxiosInstance);
private _login;
/**
* authenticate the Openmagicline instance using username/password from the instance config.
*
* if a token is passed, it will be validated and the request to `/login` will be skipped.
* @param cookies existing cookies, available after login at `.cookies`
* @returns instance for chaining
* @throws when not authenticated
*/
login: (cookies?: string[]) => Promise<Openmagicline>;
}
export { index_d as Magicline, index_d$1 as OMGL, Openmagicline, _log, unitID };

531
node_modules/openmagicline/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,531 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Openmagicline: () => Openmagicline,
_log: () => _log
});
module.exports = __toCommonJS(src_exports);
var import_axios_auth_refresh = __toESM(require("axios-auth-refresh"));
var import_axios = __toESM(require("axios"));
var import_once = __toESM(require("lodash/once"));
var import_debug = __toESM(require("debug"));
// src/util.ts
var import_form_data = __toESM(require("form-data"));
// src/constants.ts
var DEFAULT_UNIT_ID = 1;
// src/util.ts
var Util = class {
constructor(axios, mgl) {
this.axios = axios;
this.mgl = mgl;
}
async getDefaultUnitID() {
const data = await this.mgl.organization.permitted();
return data.listChildren[0].databaseId ?? DEFAULT_UNIT_ID;
}
/**
* check if the login token works.
*/
async testLogin() {
try {
await this.mgl.locale.currentLocale();
return true;
} catch {
return false;
}
}
};
var headers = (mgl) => {
const u = new URL(mgl.baseUrl);
const returnValue = {
authority: u.hostname,
"sec-ch-ua": `"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"`,
accept: `application/json, text/javascript, */*; q=0.01`,
"x-requested-with": `XMLHttpRequest`,
"sec-ch-ua-mobile": `?0`,
"user-agent": `Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36`,
origin: u.href,
"sec-fetch-site": `same-origin`,
"sec-fetch-mode": `cors`,
"sec-fetch-dest": `empty`,
referer: u.href,
"accept-language": `en-CA,en-US;q=0.9,en;q=0.8,de-DE;q=0.7,de;q=0.6,en-GB;q=0.5`
};
if (mgl.cookies)
returnValue.cookie = mgl.cookies.join("");
return returnValue;
};
var websocketHeaders = (mgl) => {
const u = new URL(mgl.baseUrl);
const returnValue = {
Pragma: "no-cache",
Origin: u.href,
"Accept-Language": "en-CA,en-US;q=0.9,en;q=0.8,de-DE;q=0.7,de;q=0.6,en-GB;q=0.5",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
"Cache-Control": "no-cache"
};
if (mgl.cookies)
returnValue.cookie = mgl.cookies.join("");
return returnValue;
};
var searchParameters = (data) => {
const parameters = new URLSearchParams();
for (const [key, value] of Object.entries(data))
parameters.set(key, value);
return parameters;
};
// src/locale.ts
var Locale = class {
constructor(axios) {
this.axios = axios;
}
async currentLocale() {
const { data } = await this.axios.get("currentLocale");
return data;
}
async supportedLocales() {
const { data } = await this.axios.get("supportedLocales");
return data;
}
};
// src/organization.ts
var Organization = class {
constructor(axios, mgl) {
this.axios = axios;
this.mgl = mgl;
}
async permitted() {
const { data } = await this.axios("organizationunit/permitted");
return data;
}
async accountInfo() {
const { data } = await this.axios("me/info");
return data;
}
async apps(unitID) {
if (!unitID)
unitID = await this.mgl.util.getDefaultUnitID();
const { data } = await this.axios("app", {
params: { organizationUnitId: unitID }
});
return data;
}
};
// src/customer.ts
var Customer = class {
constructor(axios, mgl) {
this.axios = axios;
this.mgl = mgl;
}
defaultSearchOptions = {
facility: 0,
searchInName: true,
searchInCustomerNumber: true,
searchInAddress: false,
searchInBankAccount: false,
searchInCardNumber: false,
searchInLockerKey: false,
searchInPurchasedContingentCode: false,
showAllFacilities: true,
showCheckedIn: false,
showOnlyMembers: false
};
async search(searchString, options) {
const { data } = await this.axios.post("customersearch", {
...this.defaultSearchOptions,
...options,
searchString
});
return data;
}
async getCards(customerID) {
const { data } = await this.axios(`customer/${customerID}/accessidentification`);
return data;
}
/**
* get contracts of a customer
* @param customerId customer id
* @param isActive get only active contracts (default: `true`)
*/
contract = async (customerId, isActive = true) => {
const { data } = await this.axios.get("contract", {
params: { customerId, isActive }
});
return data;
};
checkinConditions = async (customerId, organizationUnitId) => {
if (typeof organizationUnitId !== "number") {
organizationUnitId = await this.mgl.util.getDefaultUnitID();
}
const { data } = await this.axios.get(`customer/${customerId}/conditions/checkin`, {
params: { organizationUnitId }
});
return data;
};
benefits = async (customerId, active = "both") => {
const returnList = [];
if (active === true || active === "both") {
const { data } = await this.axios.get("benefitaccount", {
params: { customerId, active: true }
});
returnList.push(...data);
}
if (active === false || active === "both") {
const { data } = await this.axios.get("benefitaccount", {
params: { customerId, active: false }
});
returnList.push(...data);
}
return returnList;
};
detailedBalance = async (customerId) => {
const { data } = await this.axios.get(`customer/${customerId}/balance/detailed`);
return data;
};
// todo: implement card methods
//? removed until more card methods are implemented
// removeCard(
// customerID: Openmagicline.Customer.CustomerID,
// AccessIdentificationID: Openmagicline.Customer.AccessIdentificationID
// ): Promise<Magicline.ErrorOrSuccess> {
// return this.got(`customer/${customerID}/accessidentification/${AccessIdentificationID}`, {
// method: "DELETE",
// searchParams: {
// optLockRemote: 0,
// },
// }).json()
// }
};
// src/checkin.ts
var Checkin = class {
constructor(axios, mgl) {
this.axios = axios;
this.mgl = mgl;
}
defaultListParams = {
organizationUnitId: DEFAULT_UNIT_ID,
checkouts: false,
offset: 0,
maxResults: 25,
search: "",
filter: "",
sortedby: "checkinTime",
direction: "DESCENDING"
};
/**
* list all checked-in customers
* @param options filter, sort, etc.
*/
async list(options) {
let organizationUnitId = options?.organizationUnitId;
if (typeof organizationUnitId !== "number") {
organizationUnitId = await this.mgl.util.getDefaultUnitID();
}
const { data } = await this.axios("checkin", {
params: {
...this.defaultListParams,
organizationUnitId,
...options
}
});
return data;
}
defaultCheckinParams = {
customerCardNumber: void 0,
customerUUID: "",
fkCustomer: 0,
fkDevice: void 0,
fkOrganizationUnit: DEFAULT_UNIT_ID,
lockerKey: "",
purchasedContingentCode: void 0,
databaseId: void 0,
optlock: 0,
requiredOrganizationUnitId: DEFAULT_UNIT_ID
};
/**
* check-in a customer
*/
async checkin(options) {
let unitID = options.requiredOrganizationUnitId ?? options.fkOrganizationUnit;
if (typeof unitID !== "number") {
unitID = await this.mgl.util.getDefaultUnitID();
}
const { data } = await this.axios.post("checkin", {
...this.defaultCheckinParams,
fkOrganizationUnit: unitID,
requiredOrganizationUnitId: unitID,
...options
});
return data;
}
/**
* check-out a customer
* @param checkinId the ID of the checkin, **not** the customer ID
* @param options optional object containing optLockRemote, not sure what it does
*/
async checkout(checkinId, options) {
const { data } = await this.axios.delete(`checkin/${checkinId}`, {
params: { ...options }
});
return data;
}
defaultLockerKeyParams = {
databaseId: void 0,
optlock: 0
};
async lockerKey(checkinId, lockerKey, options) {
const { data } = await this.axios.put(`checkin/lockerkey/${checkinId}`, {
...this.defaultLockerKeyParams,
checkinId,
lockerKey,
...options
});
return data;
}
};
// src/sales.ts
var Sales = class {
constructor(axios, mgl) {
this.axios = axios;
this.mgl = mgl;
}
async products(options) {
const organizationUnitId = options?.organizationUnitId ?? await this.mgl.util.getDefaultUnitID();
const { data } = await this.axios("sales/productoverview", {
params: {
organizationUnitId,
...options
}
});
return data;
}
};
// src/socket.ts
var import_stompjs = require("@stomp/stompjs");
var import_ws = __toESM(require("ws"));
var MagicSocket = class {
constructor(mgl, unitID) {
this.mgl = mgl;
this.client = new import_stompjs.Client({
debug: this.log,
heartbeatIncoming: 1e4,
heartbeatOutgoing: 1e4,
webSocketFactory: this.webSocketFactory(mgl.baseUrl, unitID),
onWebSocketError: (
/* istanbul ignore next */
(error) => {
console.log(error.target._req.res);
throw error;
}
),
onStompError: (
/* istanbul ignore next */
(error) => {
throw error;
}
)
});
}
client;
log = _log.extend("socket");
isActive = false;
webSocketFactory = (baseUrl, unitID) => () => {
const socketURL = new URL(baseUrl);
socketURL.protocol = "wss:";
socketURL.pathname = "ws";
socketURL.searchParams.set("organizationUnitId", unitID.toString());
this.log(`connecting to ${socketURL.href}`);
return new import_ws.default(socketURL.href, [], {
headers: websocketHeaders(this.mgl)
});
};
subscriptions = {};
activate() {
if (this.isActive)
return this.isActive;
this.isActive = new Promise((resolve) => {
this.client.onConnect = resolve;
this.client.activate();
});
return this.isActive;
}
unsubscribeAll() {
for (const [key, unsubscribe] of Object.entries(this.subscriptions)) {
unsubscribe();
delete this.subscriptions[key];
}
return this.deactivate();
}
deactivate() {
this.log("deactivating STOMP");
this.isActive = false;
return this.client.deactivate();
}
deactivateAutomatically() {
if (Object.entries(this.subscriptions).length === 0)
this.deactivate();
else
this.log("there are still active subscriptions, keeping stomp active");
}
/* eslint-disable @typescript-eslint/indent */
subscribeFactory = (topic) => async (callback) => {
await this.activate();
this.client.subscribe(topic, ({ body }) => callback(JSON.parse(body)));
this.log(`subscribed to "${topic}"`);
const unsubscribe = () => {
this.client.unsubscribe(topic);
delete this.subscriptions[topic];
this.deactivateAutomatically();
};
this.subscriptions[topic] = unsubscribe;
return this.subscriptions[topic];
};
/* eslint-enable @typescript-eslint/indent */
onCheckin = this.subscribeFactory("/user/topic/checkin");
};
// src/index.ts
var _log = (0, import_debug.default)("openmagicline");
var Openmagicline = class {
// TODO: check version and warn if openmagicline is outdated
constructor(config, axios) {
this.config = config;
this.log = _log.extend(config.gym);
this.baseUrl = `https://${this.config.gym}.web.magicline.com`;
const prefixUrl = `${this.baseUrl}/rest-api`;
const httpAxiosLog = this.log.extend("http");
if (axios)
this.axios = axios;
else {
this.axios = import_axios.default.create({
baseURL: prefixUrl,
headers: headers(this)
});
}
this.axios.interceptors.request.use((config2) => {
if (this.cookies)
config2.headers.cookie = this.cookies;
return config2;
});
this.axios.interceptors.response.use((response) => {
let log = `[${response.config.method}](${response.status}) `;
log += response.config.url;
if (response.status > 200)
log += `
${response.data}`;
httpAxiosLog(log);
return response;
});
(0, import_axios_auth_refresh.default)(this.axios, () => {
console.log("request failed, refreshing token");
return this.login();
});
this.customer = new Customer(this.axios, this);
this.locale = new Locale(this.axios);
this.organization = new Organization(this.axios, this);
this.checkin = new Checkin(this.axios, this);
this.util = new Util(this.axios, this);
this.sales = new Sales(this.axios, this);
this.disposal = this.sales;
this.socket = (unitID) => new MagicSocket(this, unitID);
}
log;
axios;
baseUrl;
cookies;
customer;
locale;
organization;
/** everything related to the checkin process */
checkin;
/** miscellaneous helpers and thingies */
util;
/** everything related to retail sales (magicline calls this disposal in some places) */
sales;
/** reference to this.sales */
disposal;
/** event handler for magiclines websockets */
socket;
_login = async (cookies) => {
if (cookies) {
this.cookies = cookies;
this.login = (0, import_once.default)(this._login);
if (await this.util.testLogin()) {
return this;
} else {
this.cookies = void 0;
throw new Error("invalid token");
}
}
try {
const { username, password } = this.config;
if (!username || !password)
throw new Error("username and password need to be set when cookies aren't provided");
const response = await this.axios.post(
"login",
searchParameters({ username, password, client: "webclient" }),
// @ts-expect-error i am too lazy to fix these types ngl
{ baseURL: this.baseUrl, skipAuthRefresh: true }
);
this.login = (0, import_once.default)(this._login);
this.cookies = response.headers["set-cookie"];
} catch (error_) {
this.cookies = void 0;
const error = error_?.response?.data?.error_description ? new Error(error_.response.data.error_description) : error_;
throw error;
}
return this;
};
/**
* authenticate the Openmagicline instance using username/password from the instance config.
*
* if a token is passed, it will be validated and the request to `/login` will be skipped.
* @param cookies existing cookies, available after login at `.cookies`
* @returns instance for chaining
* @throws when not authenticated
*/
login = (0, import_once.default)(this._login);
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Openmagicline,
_log
});

21
node_modules/openmagicline/license.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-present vaaski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

82
node_modules/openmagicline/package.json generated vendored Normal file
View File

@@ -0,0 +1,82 @@
{
"name": "openmagicline",
"description": "Magicline API for everybody.",
"keywords": [
"magicline",
"gym",
"mysports",
"reverse-engineered"
],
"version": "1.1.0",
"main": "dist/index.js",
"license": "MIT",
"author": "vaaski <admin@vaa.ski>",
"repository": {
"type": "git",
"url": "https://github.com/vaaski/openmagicline.git"
},
"scripts": {
"start": "node -r dotenv/config dist",
"build": "tsup",
"prepare": "npm run build",
"dev": "nodemon",
"test": "nyc ava -s",
"test:full": "rimraf token.json && npm test",
"test:watch": "nodemon -w src -w tests -w types --exec npm test",
"coverage": "live-server coverage/lcov-report --port=8069",
"lint": "eslint . --fix --ext ts",
"format": "prettier -w types/* tests/* src/*",
"typecheck": "tsc --noEmit && tsc -p tests/tsconfig.json"
},
"files": [
"dist/**/*"
],
"ava": {
"extensions": [
"ts"
],
"files": [
"tests/**/*.test.ts"
],
"require": [
"dotenv/config"
],
"nodeArguments": [
"--loader=tsx"
],
"failFast": true,
"verbose": true
},
"devDependencies": {
"@types/debug": "^4.1.5",
"@types/lodash": "^4.14.170",
"@types/node": "^18.11.9",
"@types/ws": "^7.4.7",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"ava": "^5.1.0",
"dotenv": "^10.0.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-ava": "^13.2.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-unicorn": "^44.0.2",
"fs-jetpack": "^4.1.0",
"nodemon": "^2.0.7",
"nyc": "^15.1.0",
"prettier": "^2.3.0",
"rimraf": "^3.0.2",
"tsup": "^6.6.3",
"tsx": "^3.12.3",
"typescript": "^4.9.3"
},
"dependencies": {
"@stomp/stompjs": "^6.1.0",
"axios": "^0.21.1",
"axios-auth-refresh": "^3.3.4",
"debug": "^4.3.1",
"form-data": "^4.0.0",
"lodash": "^4.17.21",
"ws": "^7.5.3"
}
}

54
node_modules/openmagicline/readme.md generated vendored Normal file
View File

@@ -0,0 +1,54 @@
<h1><p align="center">openmagicline</p></h1>
<p align="center">The Magicline API for everybody.</p>
## synopsis
I'm working at a gym that has been using [magicline](https://magicline.com) for well over 10 years now.
When they switched to a completely new web-based interface in about 2016,
using magicline got a lot slower. The new interface not only took a
significant time to get used to, it also runs considerably slower than
the previous (native) one. This sucks for an environment where the
customer expects fast and reliable service.
Openmagicline is a **reverse-engineered**, **strongly-typed** version of
magicline's internal API.
## usage
This is far from feature-complete as I only implement the parts I need.
Feel free to use it, but expect a lot of functionality to be missing.
I am, however, very open to [contributions](#ideas) and this project should be a
solid starting point for anyone who wants to implement missing functionality.
```ts
const magicline = new Openmagicline({
gym: "gym", // example for https://gym.web.magicline.com
username: "your username",
password: "your password",
})
await magicline.login()
await magicline.customer.search("John Doe")
```
## features
- **Strongly-typed** - All API calls and responses are typed
- **Authenticates** - Automatically re-authenticates on session expiry
- **Covered** - [100% test coverage](#testing)
## ideas (contributions welcome)
- detect magicline version and warn if openmagicline is outdated
- improve tests with zod
- verify API responses with zod
## testing
I strive for 100% coverage with automated tests using [`ava`](https://npmr.vaa.ski/ava).
This is very useful for rapidly detecting changes in magicline's internal API.
There will be periodic tests set up using GitHub actions once I consider
openmagicline stable.