@unchainedshop/plugins
Version:
Official plugin collection for the Unchained Engine with payment, delivery, and pricing adapters
170 lines (169 loc) • 7.38 kB
JavaScript
import { WorkerAdapter, WorkerDirector } from '@unchainedshop/core';
const { BUDGETSMS_USERNAME, BUDGETSMS_USERID, BUDGETSMS_HANDLE } = process.env;
const BudgetSMSWorker = {
...WorkerAdapter,
key: 'shop.unchained.worker-plugin.budgetsms',
label: 'Send Messages through BudgetSMS',
version: '1.0.0',
type: 'BUDGETSMS',
doWork: async ({ from, to, text, test = false, customid, price = false, mccmnc = false, credit = false, ...params }) => {
try {
if (!BUDGETSMS_USERNAME || !BUDGETSMS_USERID || !BUDGETSMS_HANDLE) {
throw new Error('Missing BudgetSMS credentials. Please set BUDGETSMS_USERNAME, BUDGETSMS_USERID, and BUDGETSMS_HANDLE');
}
const username = BUDGETSMS_USERNAME.trim();
const userid = BUDGETSMS_USERID.trim();
const handle = BUDGETSMS_HANDLE.trim();
if (!/^\d+$/.test(userid)) {
throw new Error('BUDGETSMS_USERID must be numeric only. Found: ' + userid);
}
const url = test ? 'https://api.budgetsms.net/testsms/' : 'https://api.budgetsms.net/sendsms/';
const formattedTo = to.replace(/^\+|^00/, '').replace(/[\s-]/g, '');
const queryParams = new URLSearchParams({
username: username,
userid: userid,
handle: handle,
msg: text || '',
from: from || '',
to: formattedTo,
...(customid && { customid }),
...(!test && price && { price: '1' }),
...(!test && mccmnc && { mccmnc: '1' }),
...(!test && credit && { credit: '1' }),
...params,
});
const fullUrl = `${url}?${queryParams.toString()}`;
const response = await fetch(fullUrl, {
method: 'GET',
headers: {
Accept: 'text/plain',
'User-Agent': 'Unchained/BudgetSMS-Plugin',
},
});
const responseText = await response.text().then((text) => text.trim());
if (responseText.startsWith('OK')) {
const parts = responseText.split(' ');
const result = {
sms_id: parts[1] || null,
status: test ? 'test_successful' : 'sent',
test_mode: test,
};
if (test) {
result.message = 'Test SMS validated successfully (no credit deducted)';
}
else {
let index = 2;
if (price && parts[index] && parts[index + 1]) {
result.price = parseFloat(parts[index]);
result.parts = parseInt(parts[index + 1]);
index += 2;
}
if (mccmnc && parts[index]) {
result.mccmnc = parts[index];
index += 1;
}
if (credit && parts[index]) {
result.remaining_credit = parseFloat(parts[index]);
}
}
return {
success: true,
result,
error: null,
};
}
if (responseText.startsWith('ERR')) {
const parts = responseText.split(' ');
const errorCode = parts[1] || 'UNKNOWN';
const errorMessages = {
'1001': 'Authentication failed OR insufficient credit (BudgetSMS returns 1001 for both)',
'1002': 'Account not active',
'1003': 'Insufficient credit',
'1004': 'No access to this API',
'1005': 'No handle provided',
'1006': 'No UserID provided',
'1007': 'No Username provided',
'2001': 'SMS message text is empty',
'2002': 'SMS numeric senderid can be max. 16 numbers',
'2003': 'SMS alphanumeric sender can be max. 11 characters',
'2004': 'SMS senderid is empty or invalid',
'2005': 'Destination number is too short',
'2006': 'Destination is not numeric',
'2007': 'Destination is empty',
'2008': 'SMS text is not OK (check encoding?)',
'2009': 'Parameter issue',
'2010': 'Destination number is invalidly formatted',
'2011': 'Destination is invalid',
'2012': 'SMS message text is too long',
'2013': 'SMS message is invalid',
'2014': 'SMS CustomID is used before',
'2015': 'Charset problem',
'2016': 'Invalid UTF-8 encoding',
'3001': 'No route to destination',
'3002': 'No routes are setup',
'3003': 'Invalid destination',
'4001': 'System error, related to customid',
'4002': 'System error, temporary issue',
};
let message = errorMessages[errorCode] || `Unknown error code: ${errorCode}`;
if (errorCode === '1001') {
message += '. Note: Check your account balance - BudgetSMS may return 1001 when credit is 0.';
}
return {
success: false,
error: {
name: 'BUDGETSMS_ERROR',
code: errorCode,
message,
},
};
}
return {
success: false,
error: {
name: 'BUDGETSMS_ERROR',
message: 'Unexpected response format from BudgetSMS API',
response: responseText,
},
};
}
catch (err) {
return {
success: false,
error: {
name: err.name,
message: err.message,
stack: err.stack,
},
};
}
},
};
WorkerDirector.registerAdapter(BudgetSMSWorker);
export async function checkBudgetSmsCredentials() {
const url = 'https://api.budgetsms.net/checkcredit/';
const username = process.env.BUDGETSMS_USERNAME?.trim();
const userid = process.env.BUDGETSMS_USERID?.trim();
const handle = process.env.BUDGETSMS_HANDLE?.trim();
if (!username || !userid || !handle) {
return { error: 'Missing credentials' };
}
if (!/^\d+$/.test(userid)) {
return { error: 'UserID must be numeric only' };
}
const queryParams = new URLSearchParams({ username, userid, handle });
try {
const response = await fetch(`${url}?${queryParams.toString()}`, { method: 'GET' });
const text = await response.text();
if (text.startsWith('OK')) {
const credit = parseFloat(text.split(' ')[1]) || 0;
return { success: true, credit };
}
else {
return { error: text };
}
}
catch (err) {
return { error: err.message };
}
}