ragebuttonapi 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{index.js → api_library.js} +21 -19
- package/{cli.js → cli_entrypoint.js} +33 -56
- package/package.json +1 -29
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* const rageApi = require('ragebuttonapi');
|
|
6
6
|
*
|
|
7
7
|
* // one-time init
|
|
8
|
-
* rageApi.init({
|
|
8
|
+
* rageApi.init({ apiKey: 'rbapi_xxx' });
|
|
9
9
|
*
|
|
10
10
|
* // register rage
|
|
11
11
|
* await rageApi.rage('this stupid bug omg');
|
|
12
12
|
*
|
|
13
|
-
* // or inline config
|
|
14
|
-
* await rageApi.rage('why', {
|
|
13
|
+
* // or inline config override
|
|
14
|
+
* await rageApi.rage('why', { apiKey: '...' });
|
|
15
15
|
*
|
|
16
16
|
* // get global stats
|
|
17
17
|
* const stats = await rageApi.stats();
|
|
@@ -26,12 +26,9 @@ const https = require('https');
|
|
|
26
26
|
const http = require('http');
|
|
27
27
|
const url = require('url');
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
apiKey: null
|
|
32
|
-
};
|
|
29
|
+
const DEFAULT_SERVER = 'https://ragebutton.unaux.com';
|
|
30
|
+
let _globalConfig = { url: DEFAULT_SERVER, apiKey: null };
|
|
33
31
|
|
|
34
|
-
// ─── low-level request helper ────────────────────────────────────────────────
|
|
35
32
|
function request(endpoint, { method = 'GET', body = null } = {}) {
|
|
36
33
|
return new Promise((resolve, reject) => {
|
|
37
34
|
const parsed = url.parse(endpoint);
|
|
@@ -48,7 +45,9 @@ function request(endpoint, { method = 'GET', body = null } = {}) {
|
|
|
48
45
|
path : parsed.path,
|
|
49
46
|
method,
|
|
50
47
|
headers : {
|
|
51
|
-
'User-Agent': '
|
|
48
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
49
|
+
'Accept': 'application/json, text/plain, */*',
|
|
50
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
52
51
|
...(payload ? {
|
|
53
52
|
'Content-Type' : 'application/x-www-form-urlencoded',
|
|
54
53
|
'Content-Length': Buffer.byteLength(payload),
|
|
@@ -71,7 +70,14 @@ function request(endpoint, { method = 'GET', body = null } = {}) {
|
|
|
71
70
|
resolve(json);
|
|
72
71
|
}
|
|
73
72
|
} catch {
|
|
74
|
-
|
|
73
|
+
// Detect if server is running on InfinityFree / Unaux and blocking programmatic calls
|
|
74
|
+
if (data.includes('__test') || data.includes('aes.js') || data.includes('Javascript to work') || data.includes('testcookie')) {
|
|
75
|
+
reject(new Error(
|
|
76
|
+
'API request blocked by your hosting provider (Unaux/InfinityFree). Free hosts use a browser verification cookie (aes.js) that blocks standard CLI/API client scripts. To fix this, consider deploying your API on developer-friendly platforms like Koyeb, Render, Railway, or Vercel (for serverless PHP) which do not block API calls.'
|
|
77
|
+
));
|
|
78
|
+
} else {
|
|
79
|
+
reject(new Error(`bad response from server (status ${res.statusCode}). is ur server up?`));
|
|
80
|
+
}
|
|
75
81
|
}
|
|
76
82
|
});
|
|
77
83
|
});
|
|
@@ -82,13 +88,11 @@ function request(endpoint, { method = 'GET', body = null } = {}) {
|
|
|
82
88
|
});
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
// ─── resolve config, merging inline overrides ────────────────────────────────
|
|
86
91
|
function resolveConfig(inline = {}) {
|
|
87
92
|
const cfg = {
|
|
88
|
-
url : inline.url ?? _globalConfig.url,
|
|
93
|
+
url : inline.url ?? _globalConfig.url ?? DEFAULT_SERVER,
|
|
89
94
|
apiKey: inline.apiKey ?? _globalConfig.apiKey,
|
|
90
95
|
};
|
|
91
|
-
if (!cfg.url) throw new Error('ragebuttonapi: server url is required. call rageApi.init({ url }) or pass it inline.');
|
|
92
96
|
if (!cfg.apiKey) throw new Error('ragebuttonapi: apiKey is required. get it from ur dashboard.');
|
|
93
97
|
return { ...cfg, url: cfg.url.replace(/\/$/, '') };
|
|
94
98
|
}
|
|
@@ -97,7 +101,7 @@ function resolveConfig(inline = {}) {
|
|
|
97
101
|
|
|
98
102
|
/**
|
|
99
103
|
* One-time global config. call this at app startup.
|
|
100
|
-
* @param {{ url
|
|
104
|
+
* @param {{ url?: string, apiKey: string }} config
|
|
101
105
|
*/
|
|
102
106
|
function init(config = {}) {
|
|
103
107
|
if (config.url) _globalConfig.url = config.url.replace(/\/$/, '');
|
|
@@ -128,8 +132,7 @@ async function rage(reason, config = {}) {
|
|
|
128
132
|
* @returns {Promise<{total_rages, today_rages, user_count}>}
|
|
129
133
|
*/
|
|
130
134
|
async function stats(config = {}) {
|
|
131
|
-
const serverUrl = config.url ?? _globalConfig.url;
|
|
132
|
-
if (!serverUrl) throw new Error('ragebuttonapi: url is required for stats()');
|
|
135
|
+
const serverUrl = config.url ?? _globalConfig.url ?? DEFAULT_SERVER;
|
|
133
136
|
return request(`${serverUrl.replace(/\/$/, '')}/api/stats.php`);
|
|
134
137
|
}
|
|
135
138
|
|
|
@@ -140,9 +143,8 @@ async function stats(config = {}) {
|
|
|
140
143
|
* @returns {Promise<{ feed: Array<{user, reason, time_ago}> }>}
|
|
141
144
|
*/
|
|
142
145
|
async function feed(limit = 10, config = {}) {
|
|
143
|
-
const serverUrl = config.url ?? _globalConfig.url;
|
|
144
|
-
if (!serverUrl) throw new Error('ragebuttonapi: url is required for feed()');
|
|
146
|
+
const serverUrl = config.url ?? _globalConfig.url ?? DEFAULT_SERVER;
|
|
145
147
|
return request(`${serverUrl.replace(/\/$/, '')}/api/feed.php?limit=${limit}`);
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
module.exports = { init, rage, stats, feed };
|
|
150
|
+
module.exports = { init, rage, stats, feed };
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* install: npm install -g ragebuttonapi
|
|
5
5
|
* usage:
|
|
6
6
|
* rage → register a rage (prompts for reason)
|
|
7
|
-
* rage setup → configure server
|
|
7
|
+
* rage setup → configure api key (server is automatically set)
|
|
8
8
|
* rage "this bug is insane" → rage with inline reason
|
|
9
9
|
* rage --reason "msg" → rage with reason flag
|
|
10
10
|
* rage stats → global stats
|
|
@@ -21,24 +21,28 @@ const os = require('os');
|
|
|
21
21
|
const rageApi = require('./index.js');
|
|
22
22
|
|
|
23
23
|
const CONFIG_PATH = path.join(os.homedir(), '.ragebuttonapirc');
|
|
24
|
-
const VERSION = '1.0.
|
|
24
|
+
const VERSION = '1.0.1';
|
|
25
|
+
const DEFAULT_SERVER = 'https://ragebutton.unaux.com';
|
|
25
26
|
|
|
26
27
|
// ─── Config file helpers ──────────────────────────────────────────────────────
|
|
27
28
|
function loadConfig() {
|
|
28
29
|
try {
|
|
29
30
|
if (fs.existsSync(CONFIG_PATH)) {
|
|
30
|
-
|
|
31
|
+
const parsed = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
32
|
+
// Ensure there is always a default url
|
|
33
|
+
if (!parsed.url) {
|
|
34
|
+
parsed.url = DEFAULT_SERVER;
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
31
37
|
}
|
|
32
38
|
} catch { /* corrupt config, treat as missing */ }
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
39
|
+
return { url: DEFAULT_SERVER };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function saveConfig(cfg) {
|
|
43
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2), { mode: 0o600 });
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
// ─── Colors (no deps, raw ANSI) ───────────────────────────────────────────────
|
|
42
46
|
const c = {
|
|
43
47
|
reset : '\x1b[0m',
|
|
44
48
|
bold : '\x1b[1m',
|
|
@@ -65,7 +69,6 @@ ${dim('────────────────────────
|
|
|
65
69
|
`);
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
// ─── Prompts (pure readline, no deps) ────────────────────────────────────────
|
|
69
72
|
function prompt(question) {
|
|
70
73
|
return new Promise(resolve => {
|
|
71
74
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -76,7 +79,6 @@ function prompt(question) {
|
|
|
76
79
|
function promptPassword(question) {
|
|
77
80
|
return new Promise(resolve => {
|
|
78
81
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
79
|
-
// hide input
|
|
80
82
|
process.stdout.write(question);
|
|
81
83
|
process.stdin.setRawMode?.(true);
|
|
82
84
|
let pass = '';
|
|
@@ -105,18 +107,10 @@ function promptPassword(question) {
|
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
// ─── Setup command ────────────────────────────────────────────────────────────
|
|
109
110
|
async function cmdSetup(existing = {}) {
|
|
110
111
|
logo();
|
|
111
|
-
console.log(`${acc('Setup')} —
|
|
112
|
-
|
|
113
|
-
const defaultUrl = existing.url || '';
|
|
114
|
-
let serverUrl = await prompt(
|
|
115
|
-
`Server URL ${defaultUrl ? dim(`[${defaultUrl}]`) : ''}: `
|
|
116
|
-
);
|
|
117
|
-
if (!serverUrl && defaultUrl) serverUrl = defaultUrl;
|
|
118
|
-
if (!serverUrl) { console.log(err('\nurls cant be empty. try again.')); process.exit(1); }
|
|
119
|
-
serverUrl = serverUrl.replace(/\/$/, '');
|
|
112
|
+
console.log(`${acc('Setup')} — let's get u configured fr\n`);
|
|
113
|
+
console.log(`${dim('Server is locked to default:')} ${hl(DEFAULT_SERVER)}`);
|
|
120
114
|
|
|
121
115
|
const defaultKey = existing.apiKey ? '***' + existing.apiKey.slice(-4) : '';
|
|
122
116
|
console.log(`\nAPI Key (from ur dashboard)${defaultKey ? dim(` [${defaultKey}]`) : ''}:`);
|
|
@@ -124,33 +118,27 @@ async function cmdSetup(existing = {}) {
|
|
|
124
118
|
try {
|
|
125
119
|
apiKey = await promptPassword('> ');
|
|
126
120
|
} catch {
|
|
127
|
-
// fallback if raw mode unavailable (piped stdin etc)
|
|
128
121
|
apiKey = await prompt('API Key: ');
|
|
129
122
|
}
|
|
130
123
|
if (!apiKey && existing.apiKey) apiKey = existing.apiKey;
|
|
131
124
|
if (!apiKey) { console.log(err('\napi key cant be empty bestie.')); process.exit(1); }
|
|
132
125
|
|
|
133
|
-
const cfg = { url:
|
|
126
|
+
const cfg = { url: DEFAULT_SERVER, apiKey };
|
|
134
127
|
saveConfig(cfg);
|
|
135
128
|
|
|
136
129
|
console.log(`\n${ok('✓')} saved to ${dim(CONFIG_PATH)}`);
|
|
137
|
-
console.log(
|
|
138
|
-
console.log(` key: ${dim('***' + apiKey.slice(-4))}\n`);
|
|
130
|
+
console.log(` server: ${hl(DEFAULT_SERVER)}`);
|
|
131
|
+
console.log(` key : ${dim('***' + apiKey.slice(-4))}\n`);
|
|
139
132
|
console.log(ok(`all done! run ${hl('rage')} anytime to register ur suffering 💢\n`));
|
|
140
133
|
}
|
|
141
134
|
|
|
142
|
-
// ─── Rage command ─────────────────────────────────────────────────────────────
|
|
143
135
|
async function cmdRage(reason, cfg) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
console.log(`\n${err('add ur api key:')} ${hl('rage setup')} or set RAGEBUTTON_API_KEY env var\n`);
|
|
150
|
-
process.exit(1);
|
|
151
|
-
}
|
|
136
|
+
const serverUrl = cfg.url || DEFAULT_SERVER;
|
|
137
|
+
if (!cfg.apiKey) {
|
|
138
|
+
console.log(`\n${err('not configured yet fr.')} run ${hl('rage setup')} first.\n`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
152
141
|
|
|
153
|
-
// ask for reason if not provided inline
|
|
154
142
|
if (reason === null || reason === undefined) {
|
|
155
143
|
reason = await prompt(`${dim('what broke? (enter to skip):')} `);
|
|
156
144
|
}
|
|
@@ -158,10 +146,10 @@ if (!cfg.apiKey) {
|
|
|
158
146
|
process.stdout.write(`\n${acc('💢')} registering ur rage...`);
|
|
159
147
|
|
|
160
148
|
try {
|
|
161
|
-
rageApi.init({ url:
|
|
149
|
+
rageApi.init({ url: serverUrl, apiKey: cfg.apiKey });
|
|
162
150
|
const res = await rageApi.rage(reason, { source: 'cli' });
|
|
163
151
|
|
|
164
|
-
process.stdout.write('\r' + ' '.repeat(40) + '\r');
|
|
152
|
+
process.stdout.write('\r' + ' '.repeat(40) + '\r');
|
|
165
153
|
|
|
166
154
|
console.log(`\n ${acc('💢 RAGE REGISTERED')}\n`);
|
|
167
155
|
console.log(` ${ok('✓')} message: ${dim(res.message)}`);
|
|
@@ -192,7 +180,7 @@ if (!cfg.apiKey) {
|
|
|
192
180
|
} else if (e.code === 'daily_limit') {
|
|
193
181
|
console.log(`\n ${err('😤 daily limit hit')} — 24/24 rages today. iconic. come back tomorrow.\n`);
|
|
194
182
|
} else if (e.code === 'ECONNREFUSED' || e.code === 'ENOTFOUND') {
|
|
195
|
-
console.log(`\n ${err('❌ couldnt connect')} — is
|
|
183
|
+
console.log(`\n ${err('❌ couldnt connect')} — is the server down? (${dim(serverUrl)})\n`);
|
|
196
184
|
} else {
|
|
197
185
|
console.log(`\n ${err('💀 error:')} ${e.message}\n`);
|
|
198
186
|
}
|
|
@@ -200,14 +188,10 @@ if (!cfg.apiKey) {
|
|
|
200
188
|
}
|
|
201
189
|
}
|
|
202
190
|
|
|
203
|
-
// ─── Stats command ────────────────────────────────────────────────────────────
|
|
204
191
|
async function cmdStats(cfg) {
|
|
205
|
-
|
|
206
|
-
console.log(`\n${err('no url configured.')} run ${hl('rage setup')} first.\n`);
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
192
|
+
const serverUrl = cfg.url || DEFAULT_SERVER;
|
|
209
193
|
try {
|
|
210
|
-
const res = await rageApi.stats({ url:
|
|
194
|
+
const res = await rageApi.stats({ url: serverUrl });
|
|
211
195
|
logo();
|
|
212
196
|
console.log(` ${acc('📊 global stats')}\n`);
|
|
213
197
|
console.log(` total rages: ${hl(Number(res.total_rages).toLocaleString())}`);
|
|
@@ -220,14 +204,10 @@ async function cmdStats(cfg) {
|
|
|
220
204
|
}
|
|
221
205
|
}
|
|
222
206
|
|
|
223
|
-
// ─── Feed command ─────────────────────────────────────────────────────────────
|
|
224
207
|
async function cmdFeed(limit, cfg) {
|
|
225
|
-
|
|
226
|
-
console.log(`\n${err('no url configured.')} run ${hl('rage setup')} first.\n`);
|
|
227
|
-
process.exit(1);
|
|
228
|
-
}
|
|
208
|
+
const serverUrl = cfg.url || DEFAULT_SERVER;
|
|
229
209
|
try {
|
|
230
|
-
const res = await rageApi.feed(limit, { url:
|
|
210
|
+
const res = await rageApi.feed(limit, { url: serverUrl });
|
|
231
211
|
const feed = res.feed || [];
|
|
232
212
|
logo();
|
|
233
213
|
console.log(` ${acc('🔴 live rage feed')} ${dim(`(last ${feed.length})`)} \n`);
|
|
@@ -250,12 +230,11 @@ async function cmdFeed(limit, cfg) {
|
|
|
250
230
|
}
|
|
251
231
|
}
|
|
252
232
|
|
|
253
|
-
// ─── Help ─────────────────────────────────────────────────────────────────────
|
|
254
233
|
function showHelp() {
|
|
255
234
|
logo();
|
|
256
235
|
console.log(` ${acc('Commands')}\n`);
|
|
257
236
|
console.log(` ${hl('rage')} → register rage (asks reason interactively)`);
|
|
258
|
-
console.log(` ${hl('rage setup')} → configure
|
|
237
|
+
console.log(` ${hl('rage setup')} → configure api key`);
|
|
259
238
|
console.log(` ${hl('rage stats')} → global stats`);
|
|
260
239
|
console.log(` ${hl('rage feed')} → live rage feed`);
|
|
261
240
|
console.log(` ${hl('rage "reason here"')} → rage with inline reason`);
|
|
@@ -268,7 +247,6 @@ function showHelp() {
|
|
|
268
247
|
console.log('');
|
|
269
248
|
}
|
|
270
249
|
|
|
271
|
-
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
272
250
|
async function main() {
|
|
273
251
|
const args = process.argv.slice(2);
|
|
274
252
|
const cfg = loadConfig();
|
|
@@ -294,7 +272,6 @@ async function main() {
|
|
|
294
272
|
}
|
|
295
273
|
|
|
296
274
|
// default: rage
|
|
297
|
-
// inline reason: first non-flag, non-subcommand arg
|
|
298
275
|
const inlineReason = args.find(a => !a.startsWith('-') && a !== 'rage');
|
|
299
276
|
|
|
300
277
|
let reason;
|
|
@@ -309,4 +286,4 @@ async function main() {
|
|
|
309
286
|
main().catch(e => {
|
|
310
287
|
console.error(`\n${err('unexpected error:')} ${e.message}\n`);
|
|
311
288
|
process.exit(1);
|
|
312
|
-
});
|
|
289
|
+
});
|
package/package.json
CHANGED
|
@@ -1,29 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ragebuttonapi",
|
|
3
|
-
"version": "1.0.1",
|
|
4
|
-
"description": "official npm package + cli for RAGEBUTTONAPI™ — register developer rage from ur terminal or node app. no account needed for anon rages.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"rage": "cli.js",
|
|
8
|
-
"npm-cli": "cli.js",
|
|
9
|
-
"npmc": "cli.js"
|
|
10
|
-
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"test": "node cli.js --help"
|
|
13
|
-
},
|
|
14
|
-
"keywords": [
|
|
15
|
-
"rage",
|
|
16
|
-
"developer",
|
|
17
|
-
"api",
|
|
18
|
-
"cli",
|
|
19
|
-
"fun",
|
|
20
|
-
"mental-health",
|
|
21
|
-
"programming",
|
|
22
|
-
"anger"
|
|
23
|
-
],
|
|
24
|
-
"author": "RAGEBUTTONAPI™",
|
|
25
|
-
"license": "MIT",
|
|
26
|
-
"engines": {
|
|
27
|
-
"node": ">=16.0.0"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
{"name":"ragebuttonapi","version":"1.0.2","description":"official npm package + cli for RAGEBUTTONAPI™ — register developer rage from ur terminal or node app. no account needed for anon rages.","main":"index.js","bin":{"rage":"./cli.js"},"scripts":{"test":"node cli.js --help"},"keywords":["rage","developer","api","cli","fun","mental-health","programming","anger"],"author":"RAGEBUTTONAPI™","license":"MIT","engines":{"node":">=16.0.0"},"dependencies":{}}
|