faucetLunch.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. import { forEachAsync, generateRandomString } from '../utils'
  2. import { DBClient } from '../singletons'
  3. import { Status } from '../models/Status'
  4. import polly from 'polly-js'
  5. import { MnemonicKey } from '@initia/initia.js'
  6. import { ethers } from 'ethers'
  7. import { v4 as uuidv4 } from 'uuid'
  8. import axios, { AxiosInstance } from 'axios'
  9. import xrpl from 'xrpl'
  10. import { InitiaClient } from '../InitiaClient'
  11. import { HttpsProxyAgent } from 'https-proxy-agent'
  12. export function getAndroidModel() {
  13. const models = [
  14. 'Mi 9',
  15. 'LIO-AN00',
  16. 'PCRT00',
  17. 'V1824A',
  18. 'SM-N9760',
  19. 'HD1900',
  20. 'SKW-A0',
  21. 'NX629J',
  22. 'M973Q',
  23. 'PCLM10',
  24. 'SM-N9810',
  25. 'SM-N9860',
  26. 'SM-F9160',
  27. 'SM-A7009',
  28. 'SM-A6060',
  29. 'SM-W2018',
  30. 'SM-P610',
  31. 'VTR-L29',
  32. 'M1903F2G',
  33. 'XQ-BE72',
  34. 'RMX3709',
  35. 'SM-E5260',
  36. 'SM-W2020',
  37. 'SHARK KSR-A0',
  38. 'G8HNN',
  39. 'SM-G973U',
  40. 'SM-G973U1',
  41. 'SM-G9738',
  42. 'SM-G981B',
  43. 'SM-G988U',
  44. 'SM-G9880',
  45. 'M2012K11G',
  46. 'M2006C3LG',
  47. '21061119BI',
  48. '22127RK46C',
  49. 'M2102J20SI',
  50. 'MCE91',
  51. 'L16',
  52. 'L10',
  53. 'L12',
  54. 'L13',
  55. 'M11A',
  56. 'N11',
  57. 'N12',
  58. ]
  59. return models[Math.floor(Math.random() * models.length)]
  60. }
  61. export function getAndroidOS() {
  62. const oses = ['9', '10', '11', '12', '12.1', '13']
  63. return `Android ${oses[Math.floor(Math.random() * oses.length)]}`
  64. }
  65. export function getIOS() {
  66. const iOSes = [
  67. '14',
  68. '16.7.8',
  69. '17.5',
  70. '17.4.1',
  71. '16.7.7',
  72. '15.8.2',
  73. '17.3.1',
  74. '15.0.2',
  75. '15.1.1',
  76. '16.0.3',
  77. '16.4.1',
  78. ]
  79. return `iOS ${iOSes[Math.floor(Math.random() * iOSes.length)]}`
  80. }
  81. export function formatDateTime(date) {
  82. function pad(number, length) {
  83. return number.toString().padStart(length, '0')
  84. }
  85. const offset = -date.getTimezoneOffset()
  86. const offsetSign = offset >= 0 ? '+' : '-'
  87. const offsetHours = pad(Math.floor(Math.abs(offset) / 60), 2)
  88. const offsetMinutes = pad(Math.abs(offset) % 60, 2)
  89. return (
  90. date.getFullYear() +
  91. '-' +
  92. pad(date.getMonth() + 1, 2) +
  93. '-' +
  94. pad(date.getDate(), 2) +
  95. 'T' +
  96. pad(date.getHours(), 2) +
  97. ':' +
  98. pad(date.getMinutes(), 2) +
  99. ':' +
  100. pad(date.getSeconds(), 2) +
  101. '.' +
  102. pad(date.getMilliseconds(), 3) +
  103. offsetSign +
  104. offsetHours +
  105. offsetMinutes
  106. )
  107. }
  108. export async function getDeviceId(
  109. isAndroid: boolean,
  110. model: string,
  111. httpsAgent: any,
  112. ): Promise<string> {
  113. const deviceId = generateRandomString(22)
  114. let client: AxiosInstance
  115. if (isAndroid) {
  116. client = axios.create({
  117. httpsAgent: httpsAgent,
  118. headers: {
  119. 'X-Android-Package': 'xyz.lunchlunch.app',
  120. 'x-firebase-client':
  121. 'H4sIAAAAAAAAAKtWykhNLCpJSk0sKVayio7VUSpLLSrOzM9TslIyUqoFAFyivEQfAAAA',
  122. 'X-Android-Cert': '841CB3E1F4FC6CD420202DD419E02D4EE2E2099B',
  123. 'x-goog-api-key': 'AIzaSyA1PYciMbJ03xonKRM3JBr4yTReQ67GeuU',
  124. 'User-Agent': `Dalvik/2.1.0 (Linux; U; ${getAndroidOS()}; ${model} Build/PQ3B.190801.05281822)`,
  125. },
  126. })
  127. } else {
  128. }
  129. await client.post(
  130. 'https://firebaseinstallations.googleapis.com/v1/projects/lunchlunch-fb95e/installations',
  131. {
  132. fid: deviceId,
  133. appId: '1:383319257117:android:1a7c776df5c7048bddd6f4',
  134. authVersion: 'FIS_v2',
  135. sdkVersion: 'a:18.0.0',
  136. },
  137. )
  138. return deviceId
  139. }
  140. export async function getRawMessage(
  141. address: string,
  142. message: string,
  143. duid: string,
  144. ) {
  145. const resp = await axios.post('https://rem.mtdao.io/create', {
  146. address,
  147. message,
  148. duid,
  149. })
  150. return resp.data
  151. }
  152. async function faucetLunch(concurrency: number = 8) {
  153. const now = new Date()
  154. const accounts = await DBClient.instance.account.findMany({
  155. where: {
  156. // address: 'init1muxa5dedmr2wcc9dkd4k8d54al9gsu5szyw9rd',
  157. lastRun: {
  158. lt: new Date(now.getTime() - 24 * 60 * 60 * 1000),
  159. },
  160. status: 0,
  161. },
  162. orderBy: {
  163. id: 'asc',
  164. },
  165. })
  166. await forEachAsync(accounts, concurrency, async (account, index) => {
  167. console.log(`${index}/${accounts.length}: processing ${account.address}`)
  168. account.gmail = 'cryptoky1998@gmail.com'
  169. account.duid = 'FBF66888-C389-4C27-B832-CB49A923CED6'
  170. // account.mnemonic= 'can similar vanish left sudden vocal plate acoustic humor toast observe tortoise demand cook ankle planet angle future detail scout rookie flock type top'
  171. try {
  172. if (!account.gmail) {
  173. account.gmail = `${generateRandomString(8)}@gmail.com`
  174. }
  175. if (!account.xrpPrivateKey) {
  176. account.xrpPrivateKey = xrpl.Wallet.generate().seed
  177. }
  178. const key = new MnemonicKey({ mnemonic: account.mnemonic })
  179. // const key = new MnemonicKey()
  180. // account.duid = uuidv4().toUpperCase()
  181. // const evmWallet = new ethers.Wallet(key.privateKey.toString('hex'))
  182. const evmWallet = ethers.Wallet.fromPhrase(account.mnemonic)
  183. // const evmWallet = ethers.Wallet.createRandom()
  184. const message = JSON.stringify({
  185. signedAt: formatDateTime(new Date()),
  186. })
  187. const evmSignedMessage = await evmWallet.signMessage(message)
  188. const proxyAgent = undefined
  189. // let isAndroid = Math.random() > 0.5
  190. let isAndroid = false
  191. if (account.duid) {
  192. isAndroid = !account.duid.includes('-')
  193. }
  194. if (isAndroid && !account.deviceModel) {
  195. account.deviceModel = getAndroidModel()
  196. }
  197. if (!account.duid) {
  198. account.duid = isAndroid
  199. ? await getDeviceId(isAndroid, account.deviceModel, proxyAgent)
  200. : uuidv4().toUpperCase()
  201. }
  202. if (!account.deviceOS) {
  203. account.deviceOS = isAndroid ? getAndroidOS() : getIOS()
  204. }
  205. const rawMessage = await getRawMessage(
  206. evmWallet.address,
  207. message,
  208. account.duid,
  209. )
  210. const client = isAndroid
  211. ? axios.create({
  212. headers: {
  213. 'Lunch-Language': 'EN',
  214. 'lunch-fiat-currency': 'USD',
  215. 'lunch-app-duid': account.duid,
  216. 'lunch-app-version': '0.17.3',
  217. 'lun-app-build': 46,
  218. 'Lunch-Device-Model': account.deviceModel,
  219. 'Lunch-Device-OS': account.deviceOS,
  220. 'Lunch-Platform': 'Android',
  221. 'user-agent': `lunch/0.17.3(46) (Linux; ${account.deviceOS}; ${account.deviceModel} Build/PQ3B.190801.05281822)`,
  222. },
  223. httpsAgent: proxyAgent,
  224. })
  225. : axios.create({
  226. headers: {
  227. 'lunch-language': 'en',
  228. 'lunch-app-platform': 'iOS',
  229. 'lunch-app-version': '45',
  230. 'lunch-fiat-currency': 'USD',
  231. 'lunch-app-duid': account.duid,
  232. 'user-agent': `Lunch/1.0 (xyz.lunchlunch.app; build:45; ${account.deviceOS}) Alamofire/5.8.0`,
  233. },
  234. })
  235. const resp = await client.post(
  236. 'https://api.lunchlunch.xyz/v1/auth/sign-in',
  237. {
  238. walletAddress: evmWallet.address,
  239. signedMessage: evmSignedMessage,
  240. rawMessage: rawMessage,
  241. },
  242. )
  243. console.log(`${index}: sign-in success.`)
  244. client.defaults.headers[
  245. 'authorization'
  246. ] = `Bearer ${resp.data.accessToken}`
  247. // await client.delete('http://api.lunchlunch.xyz/v1/member/delete')
  248. // return
  249. const xrplWallet = xrpl.Wallet.fromSeed(account.xrpPrivateKey)
  250. let needRegister = false
  251. try {
  252. const memberResp = await client.get(
  253. 'https://api.lunchlunch.xyz/v1/member',
  254. )
  255. } catch (e) {
  256. if (e.response.status === 404) {
  257. needRegister = true
  258. }
  259. }
  260. if (needRegister) {
  261. const register = await client.post(
  262. 'https://api.lunchlunch.xyz/v1/member/register',
  263. {
  264. evmWalletAddress: evmWallet.address,
  265. initiaWalletAddress: key.accAddress,
  266. isImportedWallet: true,
  267. firstInitiaWalletAddress: key.accAddress,
  268. email: account.gmail,
  269. xrplWalletAddress: xrplWallet.address,
  270. },
  271. )
  272. console.log(`${index}: register done`)
  273. }
  274. await polly()
  275. .waitAndRetry(10)
  276. .executeForPromise(async info => {
  277. if (info.count) console.log(`${index}: retry ${info.count}`)
  278. try {
  279. await client.post(
  280. 'https://api.lunchlunch.xyz/v1/dish/submit-action/airdrop',
  281. {
  282. dishId: 43,
  283. },
  284. )
  285. console.log(`${index}: airdrop done`)
  286. } catch (e) {
  287. console.log(e.response.status, e.response?.data)
  288. throw e
  289. }
  290. })
  291. await DBClient.instance.account.update({
  292. where: { id: account.id },
  293. data: {
  294. status: Status.Fauceted,
  295. lastRun: new Date(),
  296. xrpPrivateKey: account.xrpPrivateKey,
  297. gmail: account.gmail,
  298. duid: account.duid,
  299. deviceModel: account.deviceModel,
  300. deviceOS: account.deviceOS,
  301. },
  302. })
  303. } catch (e) {
  304. console.log(e)
  305. await DBClient.instance.account.update({
  306. where: { id: account.id },
  307. data: {
  308. status: Status.FaucetFailed,
  309. message: e.message,
  310. lastRun: new Date(),
  311. xrpPrivateKey: account.xrpPrivateKey,
  312. gmail: account.gmail,
  313. duid: account.duid,
  314. deviceModel: account.deviceModel,
  315. deviceOS: account.deviceOS,
  316. },
  317. })
  318. }
  319. })
  320. }
  321. await faucetLunch(1)
  322. //
  323. // const phase =
  324. // 'leave bone supply chair brain thunder giant fatigue winter shrimp father stairs'
  325. // const wallet = ethers.Wallet.fromPhrase(phase)
  326. // console.log(wallet.address)
  327. // const key = new MnemonicKey({
  328. // mnemonic: phase,
  329. // })
  330. // const rawMessage =
  331. // '809a55a47f136226b26cd6f12e61c4b641895e95f7eb43851005884cd8102bac2e231d14775fa9034745491d08910a1c0d0799ddf6926397dd05e733f62bcbb4'
  332. // const signedAmino = key.createSignatureAmino()