t1nder-cli-simulator 1.1.0 → 1.2.0

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.
Files changed (2) hide show
  1. package/index.js +268 -190
  2. package/package.json +4 -2
package/index.js CHANGED
@@ -4,288 +4,366 @@ import clipboardy from 'clipboardy'
4
4
  import axios from 'axios'
5
5
  import chalk from 'chalk'
6
6
  import ora from 'ora'
7
- import figlet from 'figlet'
8
- import gradient from 'gradient-string'
9
7
  import inquirer from 'inquirer'
10
8
  import { Server } from 'socket.io'
11
9
  import { createServer } from 'http'
12
10
  import open from 'open'
13
-
11
+ import fs from 'fs'
12
+ import path from 'path'
13
+ import os from 'os'
14
+ import { HttpsProxyAgent } from 'https-proxy-agent'
15
+ import randomUseragent from 'random-useragent'
16
+
17
+ const APP_NAME = 'T1NDER'
18
+ const APP_VERSION = '2.1.0-PERSIST'
14
19
  const SECRET_PASS = 'admin123'
15
20
  const CHECK_INTERVAL = 800
16
21
  const PORT = 3456
17
22
  const CLIENT_URL = `https://t1nder.vercel.app/`
23
+ const CONFIG_FILE = path.join(os.homedir(), '.t1nder_conf.json')
24
+ const PROXY_URL = ''
18
25
 
19
26
  const SERVER_LIST = [
20
27
  {
21
- name: 'Server 1 (Ha Noi)',
22
- config: {
23
- ai: 'https://t1nder.vercel.app/api/ai',
24
- image: 'https://t1nder.vercel.app/api/image',
25
- },
28
+ name: 'HAN-01 (Ha Noi)',
29
+ ai: 'https://t1nder.vercel.app/api/ai',
30
+ upload: 'https://t1nder.vercel.app/api/upload',
26
31
  },
27
32
  {
28
- name: 'Server 2 (Thanh Hoa)',
29
- config: {
30
- ai: 'https://t1nder-vip.vercel.app/api/ai',
31
- image: 'https://t1nder-vip.vercel.app/api/image',
32
- },
33
+ name: 'TH-02 (Thanh Hoa)',
34
+ ai: 'https://t1nder-vip.vercel.app/api/ai',
35
+ upload: 'https://t1nder-vip.vercel.app/api/upload',
33
36
  },
34
37
  {
35
- name: 'Server 3 (Thai Binh)',
36
- config: {
37
- ai: 'https://tinder-server-2.vercel.app/api/ai',
38
- image: 'https://tinder-server-2.vercel.app/api/image',
39
- },
38
+ name: 'TB-03 (Thai Binh)',
39
+ ai: 'https://tinder-server-2.vercel.app/api/ai',
40
+ upload: 'https://tinder-server-2.vercel.app/api/upload',
40
41
  },
41
42
  {
42
- name: 'Server 4 (Phu Tho)',
43
- config: {
44
- ai: 'https://tinder-server-3.vercel.app/api/ai',
45
- image: 'https://tinder-server-3.vercel.app/api/image',
46
- },
43
+ name: 'PT-04 (Phu Tho)',
44
+ ai: 'https://tinder-server-3.vercel.app/api/ai',
45
+ upload: 'https://tinder-server-3.vercel.app/api/upload',
47
46
  },
48
47
  ]
49
48
 
50
- let CURRENT_API_ENDPOINT = ''
51
- let CURRENT_IMAGE_ENDPOINT = ''
52
- let CURRENT_INVITE_KEY = ''
49
+ const state = {
50
+ apiKey: '',
51
+ endpoint: '',
52
+ serverName: '',
53
+ uploadEndpoint: '',
54
+ serverIndex: 0,
55
+ isProcessing: false,
56
+ stats: {
57
+ queries: 0,
58
+ success: 0,
59
+ errors: 0,
60
+ },
61
+ }
53
62
 
54
63
  const httpServer = createServer()
55
64
  const io = new Server(httpServer, {
56
- cors: {
57
- origin: '*',
58
- methods: ['GET', 'POST'],
59
- },
65
+ cors: { origin: '*', methods: ['GET', 'POST'] },
60
66
  })
67
+ io.on('connection', () => {})
68
+
69
+ const timestamp = () => {
70
+ const now = new Date()
71
+ return now.toLocaleTimeString('en-US', { hour12: false })
72
+ }
73
+
74
+ const log = {
75
+ info: (msg) =>
76
+ console.log(
77
+ chalk.gray(`[${timestamp()}]`) + chalk.cyan(' [INFO] ') + msg
78
+ ),
79
+ success: (msg) =>
80
+ console.log(
81
+ chalk.gray(`[${timestamp()}]`) + chalk.green(' [SUCCESS] ') + msg
82
+ ),
83
+ warn: (msg) =>
84
+ console.log(
85
+ chalk.gray(`[${timestamp()}]`) + chalk.yellow(' [WARN] ') + msg
86
+ ),
87
+ error: (msg) =>
88
+ console.log(
89
+ chalk.gray(`[${timestamp()}]`) + chalk.red(' [ERROR] ') + msg
90
+ ),
91
+ system: (msg) =>
92
+ console.log(
93
+ chalk.gray(`[${timestamp()}]`) + chalk.magenta(' [SYSTEM] ') + msg
94
+ ),
95
+ }
96
+
97
+ const drawBox = (title, lines, color = 'cyan') => {
98
+ const width = 60
99
+ const c = chalk[color]
100
+ console.log(
101
+ c(`┌─ ${chalk.bold(title)} ${'─'.repeat(width - title.length - 4)}┐`)
102
+ )
103
+ lines.forEach((line) => {
104
+ const visibleLength = line.replace(/\u001b\[\d+m/g, '').length
105
+ const padding = width - visibleLength - 1
106
+ console.log(c('│ ') + line + ' '.repeat(padding > 0 ? padding : 0) + c('│'))
107
+ })
108
+ console.log(c(`└${'─'.repeat(width - 1)}┘`))
109
+ }
61
110
 
62
- io.on('connection', (socket) => {})
111
+ const mapAnswerToNumber = (char) => {
112
+ const map = { a: '1', b: '2', c: '3', d: '4', e: '5', f: '6' }
113
+ return map[char?.toLowerCase()] || '?'
114
+ }
115
+
116
+ const ConfigManager = {
117
+ save: (key, idx) => {
118
+ try {
119
+ const data = JSON.stringify({
120
+ key,
121
+ serverIndex: idx,
122
+ lastRun: new Date(),
123
+ })
124
+ fs.writeFileSync(CONFIG_FILE, data)
125
+ log.system('Session configuration saved to disk.')
126
+ } catch (e) {
127
+ log.warn('Failed to save session.')
128
+ }
129
+ },
130
+ load: () => {
131
+ try {
132
+ if (fs.existsSync(CONFIG_FILE)) {
133
+ const raw = fs.readFileSync(CONFIG_FILE)
134
+ return JSON.parse(raw)
135
+ }
136
+ } catch (e) {
137
+ return null
138
+ }
139
+ return null
140
+ },
141
+ clear: () => {
142
+ try {
143
+ if (fs.existsSync(CONFIG_FILE)) fs.unlinkSync(CONFIG_FILE)
144
+ log.system('Previous session cleared.')
145
+ } catch (e) {}
146
+ },
147
+ }
63
148
 
64
- const showBanner = () => {
149
+ const initSystem = async () => {
65
150
  console.clear()
66
- const art = figlet.textSync('Tinder', { font: 'Slant' })
67
- console.log(gradient.pastel.multiline(art))
68
- console.log(chalk.cyan(`Say hi to`), chalk.yellow('Tinder CLI.'))
69
- console.log(chalk.dim('---------------------------------------------------'))
151
+ console.log('\n')
152
+ drawBox(
153
+ APP_NAME,
154
+ [
155
+ `Version: ${APP_VERSION}`,
156
+ `System: T1nder ${APP_VERSION}`,
157
+ `Mode: ${chalk.green('SECURE & PERSISTENT')}`,
158
+ ],
159
+ 'blue'
160
+ )
161
+ console.log('')
70
162
  }
71
163
 
72
- const authenticate = async () => {
73
- const answers = await inquirer.prompt([
164
+ const authenticateAndConfigure = async () => {
165
+ const savedConfig = ConfigManager.load()
166
+
167
+ if (savedConfig) {
168
+ console.log(chalk.gray('─'.repeat(60)))
169
+ const { useSaved } = await inquirer.prompt([
170
+ {
171
+ type: 'confirm',
172
+ name: 'useSaved',
173
+ message: chalk.cyan(
174
+ `? SESSION FOUND :: Resume session for key ending in ...${savedConfig.key.slice(
175
+ -4
176
+ )}?`
177
+ ),
178
+ default: true,
179
+ },
180
+ ])
181
+
182
+ if (useSaved) {
183
+ state.apiKey = savedConfig.key
184
+ state.serverIndex = savedConfig.serverIndex
185
+
186
+ const server = SERVER_LIST[state.serverIndex] || SERVER_LIST[0]
187
+ state.serverName = server.name
188
+ state.endpoint = server.ai.trim()
189
+
190
+ log.success('Session restored successfully.')
191
+ return
192
+ } else {
193
+ ConfigManager.clear()
194
+ }
195
+ }
196
+
197
+ const { inputPass } = await inquirer.prompt([
74
198
  {
75
199
  type: 'password',
76
200
  name: 'inputPass',
77
- message: '🔒 Xác thực danh tính:',
78
- mask: '*',
201
+ message: chalk.cyan('? ACCESS CONTROL :: Enter Passphrase:'),
202
+ mask: '',
79
203
  },
80
204
  ])
81
205
 
82
- if (answers.inputPass !== SECRET_PASS) {
83
- console.log(chalk.bgRed.white.bold(' ACCESS DENIED '))
206
+ if (inputPass !== SECRET_PASS) {
207
+ log.error('Authentication Failed. Terminating process.')
84
208
  process.exit(1)
85
209
  }
86
- console.log(chalk.green('🔓 Access Granted.'))
87
- }
210
+ log.success('Identity Verified.')
88
211
 
89
- const configureSettings = async () => {
90
- console.log(chalk.yellow('\n⚙️ THIẾT LẬP HỆ THỐNG'))
212
+ console.log(chalk.gray('─'.repeat(60)))
91
213
 
92
- const keyAnswer = await inquirer.prompt([
214
+ const { key } = await inquirer.prompt([
93
215
  {
94
216
  type: 'input',
95
217
  name: 'key',
96
- message: '🔑 Nhập Invite Key:',
97
- validate: (input) =>
98
- input.trim() !== '' ? true : 'Key không được để trống!',
218
+ message: chalk.cyan('? CONFIGURATION :: Input Invite Key:'),
219
+ validate: (input) => (input.trim() !== '' ? true : 'Required field.'),
99
220
  },
100
221
  ])
101
222
 
102
- console.log(chalk.cyan('\n🌐 Danh sách Server khả dụng:'))
103
- SERVER_LIST.forEach((server, index) => {
104
- console.log(chalk.white(` [${index + 1}] ${server.name}`))
105
- })
106
- console.log('')
223
+ const choices = SERVER_LIST.map((s, i) => ({
224
+ name: `${chalk.bold(i + 1)}. ${s.name}`,
225
+ value: i,
226
+ }))
107
227
 
108
- const serverAnswer = await inquirer.prompt([
228
+ const { serverIndex } = await inquirer.prompt([
109
229
  {
110
- type: 'input',
111
- name: 'choice',
112
- message: '👉 Chọn Server (Nhập 1 hoặc 2):',
113
- default: '1',
114
- validate: (input) => {
115
- const num = parseInt(input)
116
- if (!isNaN(num) && num >= 1 && num <= SERVER_LIST.length) {
117
- return true
118
- }
119
- return `Vui lòng nhập số từ 1 đến ${SERVER_LIST.length}`
120
- },
230
+ type: 'list',
231
+ name: 'serverIndex',
232
+ message: chalk.cyan('? NETWORK :: Select Uplink Node:'),
233
+ choices: choices,
121
234
  },
122
235
  ])
123
236
 
124
- const selectedIndex = parseInt(serverAnswer.choice) - 1
125
- const selectedConfig = SERVER_LIST[selectedIndex].config
126
-
127
- CURRENT_INVITE_KEY = keyAnswer.key.trim()
128
- CURRENT_API_ENDPOINT = selectedConfig.ai.trim()
129
- CURRENT_IMAGE_ENDPOINT = selectedConfig.image.trim()
237
+ state.apiKey = key.trim()
238
+ state.serverIndex = serverIndex
239
+ state.serverName = SERVER_LIST[serverIndex].name
240
+ state.endpoint = SERVER_LIST[serverIndex].ai.trim()
130
241
 
131
- const maskedKey = CURRENT_INVITE_KEY.substring(0, 5) + '*******'
132
- console.log(chalk.dim('-----------------------------------'))
133
- console.log(chalk.blue(`Key: `) + chalk.white(maskedKey))
134
- console.log(
135
- chalk.blue(`Selected: `) + chalk.green(SERVER_LIST[selectedIndex].name)
136
- )
137
- console.log(chalk.blue(`Endpoint: `) + chalk.white(selectedIndex))
138
- console.log(chalk.green('\n✅ Configuration Loaded.\n'))
242
+ ConfigManager.save(state.apiKey, state.serverIndex)
139
243
  }
140
244
 
141
- const mapAnswerToNumber = (answerChar) => {
142
- const map = {
143
- A: '1',
144
- a: '1',
145
- B: '2',
146
- b: '2',
147
- C: '3',
148
- c: '3',
149
- D: '4',
150
- d: '4',
151
- e: '5',
152
- E: '5',
153
- f: '6',
154
- F: '6',
155
- }
156
- const key = answerChar ? answerChar.charAt(0) : ''
157
- return map[key] || '?'
245
+ const displayStatus = () => {
246
+ console.log('')
247
+ drawBox(
248
+ 'SYSTEM READY',
249
+ [
250
+ `Target: ${state.serverName}`,
251
+ `Key: ${state.apiKey.substring(0, 4)}...${state.apiKey.slice(-4)}`,
252
+ `Endpoint: Active`,
253
+ `Socket: Port ${PORT}`,
254
+ ],
255
+ 'green'
256
+ )
257
+ console.log('')
158
258
  }
159
259
 
160
- const processQuestion = async (text) => {
161
- const spinner = ora('Analyzing data...').start()
260
+ const solve = async (text) => {
261
+ state.stats.queries++
262
+ const spinner = ora({
263
+ text: chalk.gray('Processing stream data...'),
264
+ color: 'cyan',
265
+ }).start()
162
266
 
163
267
  try {
164
- try {
165
- new URL(CURRENT_API_ENDPOINT)
166
- } catch (e) {
167
- throw new Error(`Invalid URL: "${CURRENT_API_ENDPOINT}"`)
268
+ const config = {
269
+ timeout: 8000,
270
+ headers: {
271
+ 'User-Agent': randomUseragent.getRandom(),
272
+ 'Content-Type': 'application/json',
273
+ Referer: CLIENT_URL,
274
+ Origin: CLIENT_URL,
275
+ },
276
+ }
277
+
278
+ if (PROXY_URL && PROXY_URL.length > 5) {
279
+ const agent = new HttpsProxyAgent(PROXY_URL)
280
+ config.httpsAgent = agent
281
+ config.proxy = false
168
282
  }
169
283
 
170
- const response = await axios.post(
171
- CURRENT_API_ENDPOINT,
284
+ const { data } = await axios.post(
285
+ state.endpoint,
172
286
  {
173
287
  question: text,
174
- inviteKey: CURRENT_INVITE_KEY,
288
+ inviteKey: state.apiKey,
175
289
  },
176
- {
177
- timeout: 10000,
178
- headers: {
179
- 'Content-Type': 'application/json',
180
- },
181
- }
290
+ config
182
291
  )
183
292
 
184
- const apiResult = response.data
185
293
  const finalAnswer =
186
- typeof apiResult === 'object' && apiResult.answer
187
- ? apiResult.answer
188
- : apiResult
189
-
190
- const numberResult = mapAnswerToNumber(finalAnswer)
191
-
192
- spinner.succeed(chalk.green(`Analysis Complete.`))
193
-
194
- const mapData = {
195
- answer: finalAnswer,
196
- number: numberResult,
197
- }
198
-
199
- io.emit('solution_found', mapData)
200
-
201
- console.log(
202
- chalk.cyan(' [Q] '),
203
- chalk.white(text.length > 50 ? text.substring(0, 50) + '...' : text)
204
- )
205
- console.log(
206
- chalk.yellow(' [A] '),
207
- chalk.bold.green(`${finalAnswer} ➔ ${numberResult}`)
294
+ typeof data === 'object' && data.answer ? data.answer : data
295
+ const numericAnswer = mapAnswerToNumber(finalAnswer)
296
+
297
+ state.stats.success++
298
+ spinner.stop()
299
+
300
+ io.emit('solution_found', { answer: finalAnswer, number: numericAnswer })
301
+
302
+ drawBox(
303
+ `QUERY #${state.stats.queries}`,
304
+ [
305
+ chalk.gray('Q: ') +
306
+ (text.length > 55 ? text.substring(0, 52) + '...' : text),
307
+ chalk.gray(''.repeat(56)),
308
+ chalk.bold('RESULT: ') +
309
+ chalk.green(`${finalAnswer.toUpperCase()} ➔ [ ${numericAnswer} ]`),
310
+ PROXY_URL
311
+ ? chalk.dim(`(Routed via Proxy)`)
312
+ : chalk.dim(`(Direct Connection)`),
313
+ ],
314
+ 'yellow'
208
315
  )
209
316
 
210
317
  await clipboardy.write(' ')
211
- return 'CLEARED'
318
+ return true
212
319
  } catch (error) {
213
- let errMsg = error.message
214
- if (error.code === 'ERR_INVALID_URL') {
215
- errMsg = `URL lỗi: [${CURRENT_API_ENDPOINT}]`
216
- } else if (error.response) {
217
- errMsg = `Server Error (${error.response.status})`
218
- }
219
- spinner.fail(chalk.red('Lỗi: ') + errMsg)
220
- return 'ERROR'
320
+ state.stats.errors++
321
+ spinner.stop()
322
+ const msg = error.response
323
+ ? `HTTP ${error.response.status}`
324
+ : 'Connection Refused'
325
+ log.error(`Analysis Failed: ${msg}`)
326
+ return false
221
327
  }
222
328
  }
223
329
 
224
- const startClipboardWatcher = async () => {
330
+ const monitorClipboard = async () => {
225
331
  let lastClip = ''
226
332
  try {
227
333
  await clipboardy.write(' ')
228
334
  lastClip = ' '
229
335
  } catch (e) {}
230
336
 
231
- console.log(
232
- chalk.cyan(`[`),
233
- chalk.yellow(`System:`),
234
- chalk.cyan('Auto-reset enabled. Ready for Ctrl+C...'),
235
- chalk.cyan(`]`)
236
- )
337
+ log.info(`Clipboard Watcher: ${chalk.green('ENABLED')}`)
338
+ log.info(`Browser Bridge: ${chalk.green('CONNECTED')}`)
237
339
 
238
- setInterval(async () => {
340
+ const loop = setInterval(async () => {
239
341
  try {
240
- const currentClip = await clipboardy.read()
241
-
242
- if (
243
- currentClip &&
244
- currentClip !== lastClip &&
245
- currentClip.trim() !== ''
246
- ) {
247
- if (currentClip.trim().length > 3) {
248
- lastClip = currentClip
249
- const result = await processQuestion(currentClip)
250
- if (result === 'CLEARED') {
251
- lastClip = ' '
252
- }
253
- }
342
+ const current = await clipboardy.read()
343
+
344
+ if (current && current.trim().length > 3 && current !== lastClip) {
345
+ lastClip = current
346
+ const result = await solve(current)
347
+ if (result) lastClip = ' '
254
348
  }
255
349
  } catch (err) {}
256
350
  }, CHECK_INTERVAL)
257
351
  }
258
352
 
259
353
  const main = async () => {
260
- showBanner()
261
- await authenticate()
262
- await configureSettings()
354
+ await initSystem()
355
+ await authenticateAndConfigure()
356
+ displayStatus()
263
357
 
264
358
  httpServer.listen(PORT, async () => {
265
- console.log(chalk.gray(`[Signal] Broadcasting on port ${PORT}...`))
266
-
267
359
  try {
268
- console.log(chalk.yellow(`[Browser] Opening Default Browser...`))
269
360
  await open(CLIENT_URL)
270
- } catch (error) {
271
- console.log(
272
- chalk.red(`[Error] Không thể mở trình duyệt: ${error.message}`)
273
- )
361
+ } catch (e) {
362
+ log.warn('Could not auto-launch browser.')
274
363
  }
275
364
  })
276
- console.clear()
277
- console.log(
278
- chalk.cyan(`[`),
279
- chalk.yellow(`Objective:`),
280
- chalk.cyan(`Copy question to retrieve answer.`),
281
- chalk.cyan(`]`)
282
- )
283
- console.log(
284
- chalk.red.italic(
285
- `(!) Lưu ý: Clipboard sẽ tự động bị xóa sau khi lấy đáp án.`
286
- )
287
- )
288
- startClipboardWatcher()
365
+
366
+ await monitorClipboard()
289
367
  }
290
368
 
291
369
  main()
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "t1nder-cli-simulator",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Simulator for SungJinWoo system",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
- "tinder": "./index.js"
8
+ "tinder": "index.js"
9
9
  },
10
10
  "files": [
11
11
  "index.js",
@@ -27,10 +27,12 @@
27
27
  "figlet": "^1.9.4",
28
28
  "fs-extra": "^11.3.3",
29
29
  "gradient-string": "^3.0.0",
30
+ "https-proxy-agent": "^7.0.6",
30
31
  "inquirer": "^13.1.0",
31
32
  "node-notifier": "^10.0.1",
32
33
  "open": "^11.0.0",
33
34
  "ora": "^9.0.0",
35
+ "random-useragent": "^0.5.0",
34
36
  "semver": "^7.7.3",
35
37
  "socket.io": "^4.8.3",
36
38
  "update-notifier": "^7.3.1"