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.
@@ -5,13 +5,13 @@
5
5
  * const rageApi = require('ragebuttonapi');
6
6
  *
7
7
  * // one-time init
8
- * rageApi.init({ url: 'https://ragebutton.yoursite.com', apiKey: 'rbapi_xxx' });
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', { url: '...', apiKey: '...' });
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
- let _globalConfig = {
30
- url: 'https://ragebutton.unaux.com',
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': 'RAGEBUTTONAPI-npm/1.0',
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
- reject(new Error(`bad response from server (status ${res.statusCode}). is ur server up?`));
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: string, apiKey: string }} config
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 url + api key
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.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
- return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
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
- // Default URL for everyone - no setup needed
35
- return {
36
- url: 'https://ragebutton.unaux.com',
37
- apiKey: null
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')} — lets get u configured fr\n`);
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: serverUrl, apiKey };
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(`\n url: ${hl(serverUrl)}`);
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
- if (!cfg.url) {
145
- console.log(`\n${err('no server url configured.')}\n`);
146
- process.exit(1);
147
- }
148
- if (!cfg.apiKey) {
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: cfg.url, apiKey: cfg.apiKey });
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'); // clear the "registering..." line
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 ur server up? check the url in ur config (${dim('rage setup')} to edit)\n`);
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
- if (!cfg.url) {
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: cfg.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
- if (!cfg.url) {
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: cfg.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 server url + api key`);
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":{}}