reformat code

This commit is contained in:
CounterFire2023 2024-01-17 11:07:48 +08:00
parent c68ae1dcb6
commit 7d3cd73afc
51 changed files with 890 additions and 868 deletions

View File

@ -162,13 +162,16 @@ export class ApiServer {
self.setErrHandler() self.setErrHandler()
self.setFormatSend() self.setFormatSend()
self.initSchedules() self.initSchedules()
this.server.listen({ port: parseInt(process.env.API_PORT), host: process.env.API_HOST }, (err: any, address: any) => { this.server.listen(
if (err) { { port: parseInt(process.env.API_PORT), host: process.env.API_HOST },
logger.log(err) (err: any, address: any) => {
process.exit(0) if (err) {
} logger.log(err)
resolve && resolve(address) process.exit(0)
}) }
resolve && resolve(address)
},
)
}) })
} }
} }

View File

@ -1,31 +1,31 @@
import { singleton } from "decorators/singleton"; import { singleton } from 'decorators/singleton'
import { LotteryStats } from "models/LotteryStats"; import { LotteryStats } from 'models/LotteryStats'
import { formatDate } from "utils/date.util"; import { formatDate } from 'utils/date.util'
const EXPIRE_TIME = 1000 * 60 * 60; const EXPIRE_TIME = 1000 * 60 * 60
@singleton @singleton
export class LotteryCache { export class LotteryCache {
map: Map<string, typeof LotteryStats> = new Map(); map: Map<string, typeof LotteryStats> = new Map()
lastUsed: Map<string, number> = new Map(); lastUsed: Map<string, number> = new Map()
public async getData(user: string, activity: string) { public async getData(user: string, activity: string) {
const dateTag = formatDate(new Date()); const dateTag = formatDate(new Date())
if (!this.map.has(user+dateTag)) { if (!this.map.has(user + dateTag)) {
const record = await LotteryStats.insertOrUpdate({user, activity, dateTag}, {}) const record = await LotteryStats.insertOrUpdate({ user, activity, dateTag }, {})
this.map.set(user+dateTag, record); this.map.set(user + dateTag, record)
} }
this.lastUsed.set(user+dateTag, Date.now()); this.lastUsed.set(user + dateTag, Date.now())
return this.map.get(user+dateTag); return this.map.get(user + dateTag)
} }
public async flush() { public async flush() {
for (let [key, record] of this.map.entries()) { for (let [key, record] of this.map.entries()) {
// record.modifiedPaths() // record.modifiedPaths()
if (record.isModified()) { if (record.isModified()) {
await record.save(); await record.save()
} }
if (Date.now() - this.lastUsed.get(key) > EXPIRE_TIME) { if (Date.now() - this.lastUsed.get(key) > EXPIRE_TIME) {
this.map.delete(key); this.map.delete(key)
this.lastUsed.delete(key); this.lastUsed.delete(key)
} }
} }
} }

View File

@ -1,36 +1,36 @@
import { singleton } from "decorators/singleton"; import { singleton } from 'decorators/singleton'
import { FastifyRequest } from "fastify"; import { FastifyRequest } from 'fastify'
import { ZError } from "./ZError"; import { ZError } from './ZError'
@singleton @singleton
export class SyncLocker { export class SyncLocker {
map: Map<string, boolean> = new Map(); map: Map<string, boolean> = new Map()
public lock(req: FastifyRequest) { public lock(req: FastifyRequest) {
const key = `${req.method}:${req.url}:${req.user?.id || ''}` const key = `${req.method}:${req.url}:${req.user?.id || ''}`
if (this.map.has(key)) { if (this.map.has(key)) {
return false; return false
} }
this.map.set(key, true); this.map.set(key, true)
return true; return true
} }
public unlock(req: FastifyRequest) { public unlock(req: FastifyRequest) {
const key = `${req.method}:${req.url}:${req.user?.id || ''}` const key = `${req.method}:${req.url}:${req.user?.id || ''}`
this.map.delete(key); this.map.delete(key)
} }
public checkLock(req: FastifyRequest) { public checkLock(req: FastifyRequest) {
const key = `${req.method}:${req.url}:${req.user?.id || ''}` const key = `${req.method}:${req.url}:${req.user?.id || ''}`
if (this.map.has(key)) { if (this.map.has(key)) {
throw new ZError(100, 'request too fast'); throw new ZError(100, 'request too fast')
} }
this.lock(req); this.lock(req)
return true; return true
} }
public isLocked(req: FastifyRequest) { public isLocked(req: FastifyRequest) {
const key = `${req.method}:${req.url}:${req.user?.id || ''}` const key = `${req.method}:${req.url}:${req.user?.id || ''}`
return this.map.has(key); return this.map.has(key)
} }
} }

View File

@ -1,20 +1,20 @@
const ROUND = 1000000; const ROUND = 1000000
export const BOOST_CFG = [ export const BOOST_CFG = [
{ {
value: 2, value: 2,
probability: 500000 probability: 500000,
}, },
{ {
value: 3, value: 3,
probability: 300000 probability: 300000,
}, },
{ {
value: 4, value: 4,
probability: 150000 probability: 150000,
}, },
{ {
value: 5, value: 5,
probability: 50000 probability: 50000,
}, },
] ]

View File

@ -24,5 +24,5 @@ export const FUSION_CFG = {
id: 'kindle', id: 'kindle',
amount: 1, amount: 1,
}, },
] ],
} }

View File

@ -35,6 +35,6 @@ export const ALL_ITEMS: IItem[] = [
}, },
{ {
id: 'torch', id: 'torch',
name: 'Torch' name: 'Torch',
}, },
] ]

View File

@ -1,4 +1,3 @@
export const LOTTERY_CFG = { export const LOTTERY_CFG = {
start: '2024-01-01 00:00:00', start: '2024-01-01 00:00:00',
end: '2025-01-01 00:00:00', end: '2025-01-01 00:00:00',
@ -6,38 +5,37 @@ export const LOTTERY_CFG = {
{ {
item: 'usdt', item: 'usdt',
amount: 20, amount: 20,
probability: 10000 probability: 10000,
}, },
{ {
item: 'flame', item: 'flame',
amount: 30, amount: 30,
probability: 300000 probability: 300000,
}, },
{ {
item: 'thunder', item: 'thunder',
amount: 1, amount: 1,
probability: 170000 probability: 170000,
}, },
{ {
item: 'light', item: 'light',
amount: 1, amount: 1,
probability: 180000 probability: 180000,
}, },
{ {
item: 'spark', item: 'spark',
amount: 1, amount: 1,
probability: 130000 probability: 130000,
}, },
{ {
item: 'blaze', item: 'blaze',
amount: 1, amount: 1,
probability: 100000 probability: 100000,
}, },
{ {
item: 'kindle', item: 'kindle',
amount: 1, amount: 1,
probability: 10000 probability: 10000,
}, },
],
]
} }

View File

@ -1,16 +1,15 @@
import { SyncLocker } from "common/SyncLocker"; import { SyncLocker } from 'common/SyncLocker'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import BaseController, { ROLE_ANON } from "common/base.controller"; import BaseController, { ROLE_ANON } from 'common/base.controller'
import { role, router } from "decorators/router"; import { role, router } from 'decorators/router'
import { ActivityInfo } from "models/ActivityInfo"; import { ActivityInfo } from 'models/ActivityInfo'
import { ActivityUser } from "models/ActivityUser"; import { ActivityUser } from 'models/ActivityUser'
import { RedisClient } from "redis/RedisClient"; import { RedisClient } from 'redis/RedisClient'
import { rankKey } from "services/rank.svr"; import { rankKey } from 'services/rank.svr'
import { yesterday } from "utils/date.util"; import { yesterday } from 'utils/date.util'
const MAX_LIMIT = 50 const MAX_LIMIT = 50
export default class ActivityController extends BaseController { export default class ActivityController extends BaseController {
@role(ROLE_ANON) @role(ROLE_ANON)
@router('get /api/activity/:id') @router('get /api/activity/:id')
async info(req) { async info(req) {
@ -32,9 +31,9 @@ export default class ActivityController extends BaseController {
*/ */
@router('post /api/activity/upload_invite_code') @router('post /api/activity/upload_invite_code')
async uploadInviteCode(req) { async uploadInviteCode(req) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
let { code } = req.params let { code } = req.params
let user = req.user; let user = req.user
if (user.inviteUser) { if (user.inviteUser) {
throw new ZError(11, 'already set invite user') throw new ZError(11, 'already set invite user')
} }
@ -58,8 +57,8 @@ export default class ActivityController extends BaseController {
const end = start + limit - 1 const end = start + limit - 1
const records = await new RedisClient().zrevrange(`${activity}:score`, start, end) const records = await new RedisClient().zrevrange(`${activity}:score`, start, end)
let results: any = [] let results: any = []
const yesterdayKey = rankKey(activity, yesterday()); const yesterdayKey = rankKey(activity, yesterday())
for (let i = 0; i < records.length; i+=2) { for (let i = 0; i < records.length; i += 2) {
const id = records[i] const id = records[i]
let score = parseInt(records[i + 1]) let score = parseInt(records[i + 1])
const user = await ActivityUser.findById(id) const user = await ActivityUser.findById(id)
@ -76,9 +75,9 @@ export default class ActivityController extends BaseController {
address: user?.address || 'unknow', address: user?.address || 'unknow',
invite, invite,
score, score,
yesterday: yesterdayScore ? parseInt(yesterdayScore+'') : 0 yesterday: yesterdayScore ? parseInt(yesterdayScore + '') : 0,
}) })
} }
return results; return results
} }
} }

View File

@ -1,44 +1,46 @@
import { SyncLocker } from 'common/SyncLocker'
import { ZError } from 'common/ZError'
import BaseController from 'common/base.controller'
import { router } from 'decorators/router'
import { FastifyRequest } from 'fastify'
import { ActivityItem } from 'models/ActivityItem'
import { TokenClaimHistory } from 'models/TokenClaimHistory'
import { queryStakeList } from 'services/chain.svr'
import { sign } from 'utils/chain.util'
import { SyncLocker } from "common/SyncLocker";
import { ZError } from "common/ZError";
import BaseController from "common/base.controller";
import { router } from "decorators/router";
import { FastifyRequest } from "fastify";
import { ActivityItem } from "models/ActivityItem";
import { TokenClaimHistory } from "models/TokenClaimHistory";
import { queryStakeList } from "services/chain.svr";
import { sign } from "utils/chain.util";
const MAX_LIMIT = 50
export default class ChainController extends BaseController { export default class ChainController extends BaseController {
@router('get /api/stake/list') @router('get /api/stake/list')
async stakeList(req) { async stakeList(req) {
const user = req.user; const user = req.user
const records = await queryStakeList(user.address) const records = await queryStakeList(user.address)
const result = records.map((r) => r.toJson()) const result = records.map(r => r.toJson())
return result return result
} }
@router('post /api/lottery/claim_usdt') @router('post /api/lottery/claim_usdt')
async preClaimUsdt(req: FastifyRequest) { async preClaimUsdt(req: FastifyRequest) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
let user = req.user; let user = req.user
if (user.address) { if (user.address) {
throw new ZError(11, 'no address') throw new ZError(11, 'no address')
} }
const minClaimNum = +process.env.MINI_CLAIM_USDT const minClaimNum = +process.env.MINI_CLAIM_USDT
const record = await ActivityItem.findOne({user: user.id, activity: user.activity, item: 'usdt'}) const record = await ActivityItem.findOne({ user: user.id, activity: user.activity, item: 'usdt' })
if (!record || record.amount < minClaimNum) { if (!record || record.amount < minClaimNum) {
throw new ZError(10, 'no enough usdt') throw new ZError(10, 'no enough usdt')
} }
const amount = record.amount + ''; const amount = record.amount + ''
record.amount = 0; record.amount = 0
await record.save() await record.save()
const token = process.env.USDT_CONTRACT; const token = process.env.USDT_CONTRACT
const history = await TokenClaimHistory.addOne({user: user.id, activity: user.activity, address: user.address, token, amount}) const history = await TokenClaimHistory.addOne({
const res = await sign({user: user.address, token, amount, saltNonce: history.id}) user: user.id,
activity: user.activity,
address: user.address,
token,
amount,
})
const res = await sign({ user: user.address, token, amount, saltNonce: history.id })
return res return res
} }
} }

View File

@ -1,78 +1,77 @@
import { EMPTY_REWARD, ITEM_FRAME } from "common/Constants"; import { EMPTY_REWARD, ITEM_FRAME } from 'common/Constants'
import { LotteryCache } from "common/LotteryCache"; import { LotteryCache } from 'common/LotteryCache'
import { SyncLocker } from "common/SyncLocker"; import { SyncLocker } from 'common/SyncLocker'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import BaseController from "common/base.controller"; import BaseController from 'common/base.controller'
import { FUSION_CFG } from "configs/fusion"; import { FUSION_CFG } from 'configs/fusion'
import { ALL_ITEMS } from "configs/items"; import { ALL_ITEMS } from 'configs/items'
import { LOTTERY_CFG } from "configs/lottery"; import { LOTTERY_CFG } from 'configs/lottery'
import { router } from "decorators/router"; import { router } from 'decorators/router'
import { FastifyRequest } from "fastify"; import { FastifyRequest } from 'fastify'
import { ActivityItem } from "models/ActivityItem"; import { ActivityItem } from 'models/ActivityItem'
import { LotteryRecord } from "models/LotteryRecord"; import { LotteryRecord } from 'models/LotteryRecord'
import { updateRankScore } from "services/rank.svr"; import { updateRankScore } from 'services/rank.svr'
import { formatDate } from "utils/date.util"; import { formatDate } from 'utils/date.util'
const ROUND = 1000000; const ROUND = 1000000
// Get random prizes according to the set probability // Get random prizes according to the set probability
const draw = (rewards: {probability: number}[]) => { const draw = (rewards: { probability: number }[]) => {
let total = 0; let total = 0
let random = Math.floor(Math.random() * ROUND); let random = Math.floor(Math.random() * ROUND)
let reward = null; let reward = null
for (let r of rewards) { for (let r of rewards) {
total += r.probability; total += r.probability
if (random < total) { if (random < total) {
reward = r; reward = r
break; break
} }
} }
return {id: reward?.item || EMPTY_REWARD, amount: reward?.amount || 1}; return { id: reward?.item || EMPTY_REWARD, amount: reward?.amount || 1 }
} }
export default class LotteryController extends BaseController { export default class LotteryController extends BaseController {
@router('get /api/lottery/stats') @router('get /api/lottery/stats')
async userStats(req) { async userStats(req) {
let user = req.user; let user = req.user
let record = await new LotteryCache().getData(user.id, user.activity); let record = await new LotteryCache().getData(user.id, user.activity)
let result:any = record.toJson(); let result: any = record.toJson()
let items = await ActivityItem.find({user: user.id, activity: user.activity}); let items = await ActivityItem.find({ user: user.id, activity: user.activity })
result.items = items.map((i) => i.toJson()); result.items = items.map(i => i.toJson())
return result; return result
} }
@router('post /api/lottery/history') @router('post /api/lottery/history')
async userLotteryHistory(req) { async userLotteryHistory(req) {
let user = req.user; let user = req.user
let { day, page, limit } = req.params; let { day, page, limit } = req.params
const query: any = {user: user.id, activity: user.activity}; const query: any = { user: user.id, activity: user.activity }
if (day !== 'all') { if (day !== 'all') {
query.dateTag = day; query.dateTag = day
} }
page = +page || 1 page = +page || 1
limit = +limit || 10 limit = +limit || 10
let start = page * limit || 0 let start = page * limit || 0
let historys = await LotteryRecord.find(query).skip(start).limit(limit)
let historys = await LotteryRecord.find(query).skip(start).limit(limit); let total = await LotteryRecord.countDocuments(query)
let total = await LotteryRecord.countDocuments(query);
return { return {
count: total, count: total,
page: +page, page: +page,
limit, limit,
records: historys.map((h) => h.toJson()) records: historys.map(h => h.toJson()),
} }
} }
@router('get /api/lottery/items') @router('get /api/lottery/items')
async items(req) { async items(req) {
const items = ALL_ITEMS; const items = ALL_ITEMS
const cfgs = LOTTERY_CFG; const cfgs = LOTTERY_CFG
const itemMap = new Map(); const itemMap = new Map()
for (let item of items) { for (let item of items) {
itemMap.set(item.id, item); itemMap.set(item.id, item)
} }
let result = []; let result = []
for (let cfg of cfgs.rewards) { for (let cfg of cfgs.rewards) {
result.push({ result.push({
id: cfg.item, id: cfg.item,
@ -80,47 +79,46 @@ export default class LotteryController extends BaseController {
name: itemMap.get(cfg.item).name, name: itemMap.get(cfg.item).name,
}) })
} }
return {items: result, start: cfgs.start, end: cfgs.end}; return { items: result, start: cfgs.start, end: cfgs.end }
} }
@router('get /api/lottery/draw') @router('get /api/lottery/draw')
async draw(req) { async draw(req) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
let user = req.user; let user = req.user
const { start, end, rewards } = LOTTERY_CFG; const { start, end, rewards } = LOTTERY_CFG
const startTime = new Date(start).getTime(); const startTime = new Date(start).getTime()
const endTime = new Date(end).getTime(); const endTime = new Date(end).getTime()
const now = Date.now(); const now = Date.now()
if (now < startTime) { if (now < startTime) {
throw new ZError(10, 'lottery not start'); throw new ZError(10, 'lottery not start')
} }
if (now > endTime) { if (now > endTime) {
throw new ZError(11, 'lottery end'); throw new ZError(11, 'lottery end')
} }
let record = await new LotteryCache().getData(user.id, user.activity); let record = await new LotteryCache().getData(user.id, user.activity)
if (record.amount <= 0) { if (record.amount <= 0) {
throw new ZError(12, 'no chance'); throw new ZError(12, 'no chance')
} }
record.amount -= 1; record.amount -= 1
record.used += 1; record.used += 1
let reward = draw(rewards); let reward = draw(rewards)
const dateTag = formatDate(new Date()); const dateTag = formatDate(new Date())
let history = new LotteryRecord({ let history = new LotteryRecord({
user: user.id, user: user.id,
activity: user.activity, activity: user.activity,
dateTag, dateTag,
reward: reward?.id || EMPTY_REWARD, reward: reward?.id || EMPTY_REWARD,
amount: reward.amount || 0, amount: reward.amount || 0,
}); })
await history.save(); await history.save()
const items = ALL_ITEMS; const items = ALL_ITEMS
const itemMap = new Map(); const itemMap = new Map()
for (let item of items) { for (let item of items) {
itemMap.set(item.id, item); itemMap.set(item.id, item)
} }
if (itemMap.get(reward.id)?.score) { if (itemMap.get(reward.id)?.score) {
let score = (reward?.amount || 0) * itemMap.get(reward.id).score; let score = (reward?.amount || 0) * itemMap.get(reward.id).score
if (user.boost > 1 && Date.now() < user.boostExpire) { if (user.boost > 1 && Date.now() < user.boostExpire) {
score = Math.floor(score * user.boost) score = Math.floor(score * user.boost)
} }
@ -128,67 +126,72 @@ export default class LotteryController extends BaseController {
user: user.id, user: user.id,
score, score,
activity: user.activity, activity: user.activity,
scoreType: "draw", scoreType: 'draw',
scoreParams: { scoreParams: {
date: dateTag date: dateTag,
} },
}) })
} }
await ActivityItem.insertOrUpdate({ await ActivityItem.insertOrUpdate(
user: user.id, {
activity: user.activity, user: user.id,
item: reward.id activity: user.activity,
}, item: reward.id,
{ },
$inc: {amount: reward.amount}, {
last: Date.now() $inc: { amount: reward.amount },
}) last: Date.now(),
return reward; },
)
return reward
} }
@router('get /api/lottery/fusion') @router('get /api/lottery/fusion')
async fusion(req) { async fusion(req) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
let user = req.user; let user = req.user
let items = await ActivityItem.find({user: user.id, activity: user.activity}); let items = await ActivityItem.find({ user: user.id, activity: user.activity })
let itemCountMap = new Map(); let itemCountMap = new Map()
for (let item of items) { for (let item of items) {
itemCountMap.set(item.item, item.amount); itemCountMap.set(item.item, item.amount)
} }
for (let item of FUSION_CFG.source) { for (let item of FUSION_CFG.source) {
if (!itemCountMap.has(item.id)) { if (!itemCountMap.has(item.id)) {
throw new ZError(13, 'no enough item'); throw new ZError(13, 'no enough item')
} }
if (itemCountMap.get(item.id) < item.amount) { if (itemCountMap.get(item.id) < item.amount) {
throw new ZError(14, 'no enough item'); throw new ZError(14, 'no enough item')
} }
} }
for (let item of FUSION_CFG.source) { for (let item of FUSION_CFG.source) {
await ActivityItem.insertOrUpdate({ await ActivityItem.insertOrUpdate(
user: user.id, {
activity: user.activity, user: user.id,
item: item.id activity: user.activity,
}, item: item.id,
{ },
$inc: {amount: -item.amount}, {
last: Date.now() $inc: { amount: -item.amount },
}) last: Date.now(),
},
)
} }
await ActivityItem.insertOrUpdate({ await ActivityItem.insertOrUpdate(
user: user.id, {
activity: user.activity, user: user.id,
item: FUSION_CFG.target.id activity: user.activity,
}, item: FUSION_CFG.target.id,
{ },
$inc: {amount: FUSION_CFG.target.amount}, {
last: Date.now() $inc: { amount: FUSION_CFG.target.amount },
}) last: Date.now(),
},
)
return { return {
id: FUSION_CFG.target.id, id: FUSION_CFG.target.id,
amount: FUSION_CFG.target.amount, amount: FUSION_CFG.target.amount,
} }
} }
} }

View File

@ -1,33 +1,33 @@
import BaseController, {ROLE_ANON} from 'common/base.controller' import BaseController, { ROLE_ANON } from 'common/base.controller'
import { SyncLocker } from 'common/SyncLocker' import { SyncLocker } from 'common/SyncLocker'
import {ZError} from 'common/ZError' import { ZError } from 'common/ZError'
import { BOOST_CFG } from 'configs/boost' import { BOOST_CFG } from 'configs/boost'
import { role, router } from 'decorators/router' import { role, router } from 'decorators/router'
import logger from 'logger/logger' import logger from 'logger/logger'
import { ActivityUser } from 'models/ActivityUser' import { ActivityUser } from 'models/ActivityUser'
import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord' import { DEFAULT_EXPIRED, NonceRecord } from 'models/NonceRecord'
import { ScoreRecord } from 'models/ScoreRecord' import { ScoreRecord } from 'models/ScoreRecord'
import { LoginRecordQueue } from 'queue/loginrecord.queue' import { LoginRecordQueue } from 'queue/loginrecord.queue'
import { RedisClient } from 'redis/RedisClient' import { RedisClient } from 'redis/RedisClient'
import { queryCheckInList } from 'services/chain.svr' import { queryCheckInList } from 'services/chain.svr'
import { rankKey } from 'services/rank.svr' import { rankKey } from 'services/rank.svr'
import {SiweMessage} from 'siwe' import { SiweMessage } from 'siwe'
import { nextday, yesterday } from 'utils/date.util' import { nextday, yesterday } from 'utils/date.util'
import { checkParamsNeeded } from 'utils/net.util' import { checkParamsNeeded } from 'utils/net.util'
import { aesDecrypt, base58ToHex } from 'utils/security.util' import { aesDecrypt, base58ToHex } from 'utils/security.util'
const LOGIN_TIP = 'This signature is just to verify your identity' const LOGIN_TIP = 'This signature is just to verify your identity'
const ROUND = 1000000; const ROUND = 1000000
const generateBoost = (rewards: {probability: number}[]) => { const generateBoost = (rewards: { probability: number }[]) => {
let total = 0; let total = 0
let random = Math.floor(Math.random() * ROUND); let random = Math.floor(Math.random() * ROUND)
let reward = null; let reward = null
for (let r of rewards) { for (let r of rewards) {
total += r.probability; total += r.probability
if (random < total) { if (random < total) {
reward = r; reward = r
break; break
} }
} }
return reward.value return reward.value
@ -48,19 +48,19 @@ class SignController extends BaseController {
const { signature, message, activity } = req.params const { signature, message, activity } = req.params
checkParamsNeeded(signature, message) checkParamsNeeded(signature, message)
if (!message.nonce) { if (!message.nonce) {
throw new ZError(11, 'Invalid nonce'); throw new ZError(11, 'Invalid nonce')
} }
let nonce = message.nonce let nonce = message.nonce
let source = 'unknow' let source = 'unknow'
if (nonce.length > 24) { if (nonce.length > 24) {
nonce = base58ToHex(nonce); nonce = base58ToHex(nonce)
let nonceStr = aesDecrypt(nonce, activity); let nonceStr = aesDecrypt(nonce, activity)
if (nonceStr.indexOf('|') >=0 ) { if (nonceStr.indexOf('|') >= 0) {
const split = nonceStr.split('|') const split = nonceStr.split('|')
nonce = split[0]; nonce = split[0]
source = split[1]; source = split[1]
} else { } else {
nonce = nonceStr; nonce = nonceStr
} }
} }
let record = await NonceRecord.findById(nonce) let record = await NonceRecord.findById(nonce)
@ -75,9 +75,9 @@ class SignController extends BaseController {
} }
record.status = 1 record.status = 1
await record.save() await record.save()
const msgSign = new SiweMessage(message); const msgSign = new SiweMessage(message)
try { try {
await msgSign.verify({ signature, nonce: message.nonce }); await msgSign.verify({ signature, nonce: message.nonce })
} catch (e) { } catch (e) {
throw new ZError(15, 'signature invalid') throw new ZError(15, 'signature invalid')
} }
@ -92,16 +92,20 @@ class SignController extends BaseController {
await accountData.save() await accountData.save()
new LoginRecordQueue().addLog(req, accountData.id, activity, source) new LoginRecordQueue().addLog(req, accountData.id, activity, source)
const token = await res.jwtSign({ id: accountData.id, address: accountData.address, activity: accountData.activity }) const token = await res.jwtSign({
id: accountData.id,
address: accountData.address,
activity: accountData.activity,
})
return { token } return { token }
} }
@router('get /api/user/state') @router('get /api/user/state')
async userInfo(req){ async userInfo(req) {
const user = req.user; const user = req.user
const todayKey = rankKey(user.activity, new Date()); const todayKey = rankKey(user.activity, new Date())
const todayScore = await new RedisClient().zscore(todayKey, user.id) const todayScore = await new RedisClient().zscore(todayKey, user.id)
const totalKey = rankKey(user.activity); const totalKey = rankKey(user.activity)
const totalScore = await new RedisClient().zscore(totalKey, user.id) const totalScore = await new RedisClient().zscore(totalKey, user.id)
const totalRank = await new RedisClient().zrevrank(totalKey, user.id) const totalRank = await new RedisClient().zrevrank(totalKey, user.id)
let invite = '' let invite = ''
@ -112,9 +116,9 @@ class SignController extends BaseController {
} }
} }
const records = await ScoreRecord.find({ user: user.id, activity: user.activity, scoreType: 'invite' }) const records = await ScoreRecord.find({ user: user.id, activity: user.activity, scoreType: 'invite' })
let score = 0; let score = 0
for (let record of records) { for (let record of records) {
score += record.score; score += record.score
} }
let result = { let result = {
address: user.address, address: user.address,
@ -124,34 +128,34 @@ class SignController extends BaseController {
twitterName: user.twitterName, twitterName: user.twitterName,
discordId: user.discordId, discordId: user.discordId,
discordName: user.discordName, discordName: user.discordName,
scoreToday: todayScore ? parseInt(todayScore+'') : 0, scoreToday: todayScore ? parseInt(todayScore + '') : 0,
scoreTotal: totalScore ? parseInt(totalScore+'') : 0, scoreTotal: totalScore ? parseInt(totalScore + '') : 0,
rankTotal: totalRank ? totalRank : '-', rankTotal: totalRank ? totalRank : '-',
invite, invite,
inviteCount: records.length, inviteCount: records.length,
inviteScore: score, inviteScore: score,
code: user.inviteCode, code: user.inviteCode,
} }
return result; return result
} }
@router('get /api/user/state/boost') @router('get /api/user/state/boost')
async boost(req){ async boost(req) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
const user = req.user; const user = req.user
if (user.boost > 1 && user.boostExpire && user.boostExpire > Date.now()) { if (user.boost > 1 && user.boostExpire && user.boostExpire > Date.now()) {
throw new ZError(11, 'already boosted') throw new ZError(11, 'already boosted')
} }
user.boost = generateBoost(BOOST_CFG) user.boost = generateBoost(BOOST_CFG)
user.boostExpire = nextday(); user.boostExpire = nextday()
await user.save(); await user.save()
return { boost: user.boost, boostExpire: user.boostExpire }; return { boost: user.boost, boostExpire: user.boostExpire }
} }
@router('get /api/user/checkin/list/:tag') @router('get /api/user/checkin/list/:tag')
async checkInList(req){ async checkInList(req) {
const user = req.user; const user = req.user
const {tag} = req.params; const { tag } = req.params
let days: any = 1; let days: any = 1
if (tag === '1month') { if (tag === '1month') {
days = '1month' days = '1month'
} else if (tag === 'last') { } else if (tag === 'last') {
@ -161,7 +165,6 @@ class SignController extends BaseController {
return res.data return res.data
} }
/** /**
* regist user by token from wallet-svr * regist user by token from wallet-svr
* TODO:: * TODO::
@ -175,5 +178,4 @@ class SignController extends BaseController {
const { wallet_token } = req.params const { wallet_token } = req.params
return {} return {}
} }
} }

View File

@ -1,40 +1,39 @@
import { SyncLocker } from "common/SyncLocker"; import { SyncLocker } from 'common/SyncLocker'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import BaseController from "common/base.controller"; import BaseController from 'common/base.controller'
import { router } from "decorators/router"; import { router } from 'decorators/router'
import { TaskCfg, TaskTypeEnum } from "models/ActivityInfo"; import { TaskCfg, TaskTypeEnum } from 'models/ActivityInfo'
import { TaskStatus, TaskStatusEnum } from "models/ActivityUser"; import { TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
import { join } from 'path' import { join } from 'path'
import { formatDate } from "utils/date.util"; import { formatDate } from 'utils/date.util'
const fs = require('fs') const fs = require('fs')
const prod = process.env.NODE_ENV === 'production' const prod = process.env.NODE_ENV === 'production'
const initTasks = () => { const initTasks = () => {
const tasks = join(__dirname, '../tasks') const tasks = join(__dirname, '../tasks')
const list = new Map(); const list = new Map()
fs.readdirSync(tasks) fs.readdirSync(tasks)
.filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/)) .filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/))
.forEach((file: any) => { .forEach((file: any) => {
const Task = require('../tasks/' + file); const Task = require('../tasks/' + file)
list.set(Task.default.name, { list.set(Task.default.name, {
name: Task.default.name, name: Task.default.name,
desc: Task.default.desc, desc: Task.default.desc,
show: Task.default.show}) show: Task.default.show,
}) })
return list })
return list
} }
const allTasks = initTasks(); const allTasks = initTasks()
export default class TasksController extends BaseController { export default class TasksController extends BaseController {
@router('post /api/tasks/progress') @router('post /api/tasks/progress')
async taskProgress(req) { async taskProgress(req) {
let user = req.user; let user = req.user
let activity = req.activity; let activity = req.activity
const dateTag = formatDate(new Date()); const dateTag = formatDate(new Date())
let taskAddedSet = new Set(); let taskAddedSet = new Set()
for (let task of user.taskProgress) { for (let task of user.taskProgress) {
if (task.dateTag) { if (task.dateTag) {
taskAddedSet.add(task.id + ':' + task.dateTag) taskAddedSet.add(task.id + ':' + task.dateTag)
@ -42,52 +41,51 @@ export default class TasksController extends BaseController {
taskAddedSet.add(task.id) taskAddedSet.add(task.id)
} }
} }
let modified = false; let modified = false
let visibleTasks = new Set(); let visibleTasks = new Set()
for (let task of activity.tasks) { for (let task of activity.tasks) {
if (!allTasks.has(task.task)) { if (!allTasks.has(task.task)) {
continue; continue
} }
if (!task.isVaild()) { if (!task.isVaild()) {
continue; continue
} }
if (task.type === TaskTypeEnum.DAILY ) { if (task.type === TaskTypeEnum.DAILY) {
let id = `${task.id}:${dateTag}` let id = `${task.id}:${dateTag}`
if (!taskAddedSet.has(id)) { if (!taskAddedSet.has(id)) {
modified = true; modified = true
user.taskProgress.push({id, task:task.task ,dateTag: dateTag, status: TaskStatusEnum.NOT_START}) user.taskProgress.push({ id, task: task.task, dateTag: dateTag, status: TaskStatusEnum.NOT_START })
} }
if (task.show) visibleTasks.add(id); if (task.show) visibleTasks.add(id)
} else if (task.type === TaskTypeEnum.ONCE ) { } else if (task.type === TaskTypeEnum.ONCE) {
if (!taskAddedSet.has(task.id)) { if (!taskAddedSet.has(task.id)) {
modified = true; modified = true
user.taskProgress.push({id: task.id, task: task.task, status: TaskStatusEnum.NOT_START}) user.taskProgress.push({ id: task.id, task: task.task, status: TaskStatusEnum.NOT_START })
} }
if (task.show) visibleTasks.add(task.id); if (task.show) visibleTasks.add(task.id)
} }
} }
if (modified) { if (modified) {
await user.save(); await user.save()
} }
return user.taskProgress.filter((t: TaskStatus) => visibleTasks.has(t.id)); return user.taskProgress.filter((t: TaskStatus) => visibleTasks.has(t.id))
} }
@router('post /api/tasks/begin_task') @router('post /api/tasks/begin_task')
async beginTask(req) { async beginTask(req) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
let user = req.user; let user = req.user
let activity = req.activity; let activity = req.activity
let { task } = req.params; let { task } = req.params
const [taskId, dateTag] = task.split(':'); const [taskId, dateTag] = task.split(':')
const currentDateTag = formatDate(new Date()); const currentDateTag = formatDate(new Date())
if (dateTag && currentDateTag !== dateTag) { if (dateTag && currentDateTag !== dateTag) {
throw new ZError(11, 'task date not match') throw new ZError(11, 'task date not match')
} }
if (!activity.isVaild()) { if (!activity.isVaild()) {
throw new ZError(15, 'activity not start or end') throw new ZError(15, 'activity not start or end')
} }
let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId); let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId)
if (!cfg) { if (!cfg) {
throw new ZError(12, 'task not found') throw new ZError(12, 'task not found')
} }
@ -105,80 +103,88 @@ export default class TasksController extends BaseController {
return t.id.indexOf(preTask) > -1 return t.id.indexOf(preTask) > -1
} }
return t.id === preTask return t.id === preTask
}); })
if (!preTaskData || (preTaskData.status === TaskStatusEnum.NOT_START || preTaskData.status === TaskStatusEnum.RUNNING)) { if (
!preTaskData ||
preTaskData.status === TaskStatusEnum.NOT_START ||
preTaskData.status === TaskStatusEnum.RUNNING
) {
throw new ZError(14, 'task previous not finish') throw new ZError(14, 'task previous not finish')
} }
} }
} }
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task)
if (currentTask.status === TaskStatusEnum.PART_SUCCESS || currentTask.status === TaskStatusEnum.SUCCESS || currentTask.status === TaskStatusEnum.CLAIMED) { if (
currentTask.status === TaskStatusEnum.PART_SUCCESS ||
currentTask.status === TaskStatusEnum.SUCCESS ||
currentTask.status === TaskStatusEnum.CLAIMED
) {
throw new ZError(15, 'task already end') throw new ZError(15, 'task already end')
} }
if (currentTask.status === TaskStatusEnum.NOT_START) { if (currentTask.status === TaskStatusEnum.NOT_START) {
currentTask.timeStart = Date.now(); currentTask.timeStart = Date.now()
currentTask.status = TaskStatusEnum.RUNNING; currentTask.status = TaskStatusEnum.RUNNING
} }
await user.save(); await user.save()
return currentTask return currentTask
} }
@router('post /api/tasks/check_task') @router('post /api/tasks/check_task')
async checkTask(req) { async checkTask(req) {
const user = req.user; const user = req.user
const activity = req.activity; const activity = req.activity
const { task } = req.params; const { task } = req.params
const [taskId, dateTag] = task.split(':'); const [taskId, dateTag] = task.split(':')
const currentDateTag = formatDate(new Date()); const currentDateTag = formatDate(new Date())
if (dateTag && currentDateTag !== dateTag) { if (dateTag && currentDateTag !== dateTag) {
throw new ZError(11, 'task date not match') throw new ZError(11, 'task date not match')
} }
if (!activity.isVaild()) { if (!activity.isVaild()) {
throw new ZError(15, 'activity not start or end') throw new ZError(15, 'activity not start or end')
} }
let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId); let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId)
if (!cfg) { if (!cfg) {
throw new ZError(12, 'task not found') throw new ZError(12, 'task not found')
} }
if (!cfg.isVaild()) { if (!cfg.isVaild()) {
throw new ZError(16, 'task not start or end') throw new ZError(16, 'task not start or end')
} }
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task)
if (!currentTask) { if (!currentTask) {
throw new ZError(11, 'task not found') throw new ZError(11, 'task not found')
} }
if (currentTask.status === TaskStatusEnum.CLAIMED) { if (currentTask.status === TaskStatusEnum.CLAIMED) {
return currentTask; return currentTask
} }
if (currentTask.status === TaskStatusEnum.NOT_START) { if (currentTask.status === TaskStatusEnum.NOT_START) {
throw new ZError(11, 'task not begin'); throw new ZError(11, 'task not begin')
} }
if (currentTask.status === TaskStatusEnum.RUNNING || currentTask.status === TaskStatusEnum.PART_SUCCESS) { if (currentTask.status === TaskStatusEnum.RUNNING || currentTask.status === TaskStatusEnum.PART_SUCCESS) {
let Task = require('../tasks/' + currentTask.task); let Task = require('../tasks/' + currentTask.task)
let taskInstance = new Task.default({user, activity}); let taskInstance = new Task.default({ user, activity })
await taskInstance.execute({task: currentTask}); await taskInstance.execute({ task: currentTask })
} }
return currentTask return currentTask
} }
@router('post /api/tasks/claim') @router('post /api/tasks/claim')
async claimTask(req) { async claimTask(req) {
new SyncLocker().checkLock(req); new SyncLocker().checkLock(req)
const user = req.user; const user = req.user
const activity = req.activity; const activity = req.activity
const { task } = req.params; const { task } = req.params
const [taskId, dateTag] = task.split(':'); const [taskId, dateTag] = task.split(':')
if (!activity.isVaild()) { if (!activity.isVaild()) {
throw new ZError(15, 'activity not start or end') throw new ZError(15, 'activity not start or end')
} }
let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId); let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId)
if (!cfg) { if (!cfg) {
throw new ZError(14, 'task not found') throw new ZError(14, 'task not found')
} }
if (!cfg.isVaild()) { if (!cfg.isVaild()) {
throw new ZError(16, 'task not start or end') throw new ZError(16, 'task not start or end')
} }
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task)
if (!currentTask) { if (!currentTask) {
throw new ZError(11, 'task not found') throw new ZError(11, 'task not found')
} }
@ -189,10 +195,9 @@ export default class TasksController extends BaseController {
throw new ZError(13, 'task not end') throw new ZError(13, 'task not end')
} }
const Task = require('../tasks/' + currentTask.task); const Task = require('../tasks/' + currentTask.task)
const taskInstance = new Task.default({user, activity}); const taskInstance = new Task.default({ user, activity })
await taskInstance.claimReward(currentTask); await taskInstance.claimReward(currentTask)
return currentTask return currentTask
} }
} }

View File

@ -7,7 +7,6 @@ const dir = `${__dirname}/../initdatas`
const db = mongoose.connection const db = mongoose.connection
var ejson = require('mongodb-extended-json') var ejson = require('mongodb-extended-json')
;(async () => { ;(async () => {
try { try {
await mongoose.connect(process.env.DB_MAIN) await mongoose.connect(process.env.DB_MAIN)
@ -18,7 +17,7 @@ var ejson = require('mongodb-extended-json')
const collection_name = list[i].replace('.json', '') const collection_name = list[i].replace('.json', '')
const parsedJSON = require(dir + '/' + list[i]) const parsedJSON = require(dir + '/' + list[i])
const jsonArry = ejson.deserialize(parsedJSON) const jsonArry = ejson.deserialize(parsedJSON)
await db.collection(collection_name).insertMany(jsonArry); await db.collection(collection_name).insertMany(jsonArry)
console.log('Inserted ' + jsonArry.length + ' documents into the "' + collection_name + '" collection.') console.log('Inserted ' + jsonArry.length + ' documents into the "' + collection_name + '" collection.')
} }
console.log('Finished inserting documents into the database') console.log('Finished inserting documents into the database')
@ -27,4 +26,3 @@ var ejson = require('mongodb-extended-json')
} }
process.exit(0) process.exit(0)
})() })()

View File

@ -5,12 +5,11 @@ import findOrCreate from 'mongoose-findorcreate'
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses' import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'
import { BaseModule } from './Base' import { BaseModule } from './Base'
export enum TaskTypeEnum { export enum TaskTypeEnum {
ONCE = 1, ONCE = 1,
DAILY = 2, DAILY = 2,
} }
@modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }, }) @modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW } })
export class TaskCfg { export class TaskCfg {
@prop() @prop()
id: string id: string
@ -20,19 +19,19 @@ export class TaskCfg {
title: string title: string
@prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE }) @prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE })
type: TaskTypeEnum type: TaskTypeEnum
@prop({default: 1}) @prop({ default: 1 })
repeat: number repeat: number
@prop() @prop()
desc: string desc: string
@prop({ type: mongoose.Schema.Types.Mixed }) @prop({ type: mongoose.Schema.Types.Mixed })
category: any category: any
@prop({default: false}) @prop({ default: false })
autoclaim: boolean autoclaim: boolean
@prop({ type: () => [String], default: [] }) @prop({ type: () => [String], default: [] })
pretasks: string[] pretasks: string[]
@prop() @prop()
score: number score: number
@prop({default: true}) @prop({ default: true })
show: boolean show: boolean
@prop({ type: mongoose.Schema.Types.Mixed }) @prop({ type: mongoose.Schema.Types.Mixed })
cfg: any cfg: any
@ -135,7 +134,6 @@ class ActivityInfoClass extends BaseModule {
result.tasks = tasks result.tasks = tasks
return result return result
} }
} }
export const ActivityInfo = getModelForClass(ActivityInfoClass, { existingConnection: ActivityInfoClass.db }) export const ActivityInfo = getModelForClass(ActivityInfoClass, { existingConnection: ActivityInfoClass.db })

View File

@ -6,9 +6,12 @@ import { BaseModule } from './Base'
/** /**
*/ */
@dbconn() @dbconn()
@index({ user: 1, activity: 1}, { unique: false }) @index({ user: 1, activity: 1 }, { unique: false })
@index({ user: 1, activity: 1, item: 1 }, { unique: true }) @index({ user: 1, activity: 1, item: 1 }, { unique: true })
@modelOptions({ schemaOptions: { collection: 'activity_item', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'activity_item', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class ActivityItemClass extends BaseModule { class ActivityItemClass extends BaseModule {
@prop() @prop()
public user: string public user: string
@ -16,16 +19,16 @@ class ActivityItemClass extends BaseModule {
public activity: string public activity: string
@prop() @prop()
public item: string public item: string
@prop({default: 0}) @prop({ default: 0 })
public amount: number public amount: number
@prop() @prop()
public last: number public last: number
public toJson() { public toJson() {
return { return {
id: this.item, id: this.item,
amount: this.amount amount: this.amount,
} }
} }
} }
export const ActivityItem = getModelForClass(ActivityItemClass, { existingConnection: ActivityItemClass['db'] }) export const ActivityItem = getModelForClass(ActivityItemClass, { existingConnection: ActivityItemClass['db'] })

View File

@ -1,4 +1,13 @@
import { getModelForClass, index, modelOptions, mongoose, pre, prop, ReturnModelType, Severity } from '@typegoose/typegoose' import {
getModelForClass,
index,
modelOptions,
mongoose,
pre,
prop,
ReturnModelType,
Severity,
} from '@typegoose/typegoose'
import { dbconn } from 'decorators/dbconn' import { dbconn } from 'decorators/dbconn'
// @ts-ignore // @ts-ignore
import findOrCreate from 'mongoose-findorcreate' import findOrCreate from 'mongoose-findorcreate'
@ -16,7 +25,7 @@ export enum TaskStatusEnum {
PART_SUCCESS = 4, PART_SUCCESS = 4,
} }
@modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }}) @modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW } })
export class TaskStatus { export class TaskStatus {
@prop() @prop()
id: string id: string
@ -44,18 +53,21 @@ interface ActivityUserClass extends Base, TimeStamps {}
@index({ inviteUser: 1, activity: 1 }, { unique: false }) @index({ inviteUser: 1, activity: 1 }, { unique: false })
@index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $exists: true } } }) @index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $exists: true } } })
@index({ discordId: 1 }, { unique: true, partialFilterExpression: { discordId: { $exists: true } } }) @index({ discordId: 1 }, { unique: true, partialFilterExpression: { discordId: { $exists: true } } })
@modelOptions({ schemaOptions: { collection: 'activity_user', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'activity_user', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
@pre<ActivityUserClass>('save', async function () { @pre<ActivityUserClass>('save', async function () {
if (!this.inviteCode) { if (!this.inviteCode) {
// 取ObjectId的time和inc段, // 取ObjectId的time和inc段,
// 将time段倒序(倒序后, 如果以0开始, 则移除0, 随机拼接一个hex字符), 然后拼接inc段, 再转换成52进制 // 将time段倒序(倒序后, 如果以0开始, 则移除0, 随机拼接一个hex字符), 然后拼接inc段, 再转换成52进制
let timeStr = this.id.slice(0, 8).split("").reverse().join(""); let timeStr = this.id.slice(0, 8).split('').reverse().join('')
if (timeStr.indexOf('0') === 0) { if (timeStr.indexOf('0') === 0) {
let randomStr = convert({ numStr: (Math.random() * 51 | 0 + 1) + '', base:10, to: 52}) let randomStr = convert({ numStr: ((Math.random() * 51) | (0 + 1)) + '', base: 10, to: 52 })
timeStr = randomStr + timeStr.slice(1) timeStr = randomStr + timeStr.slice(1)
} }
let shortId = timeStr + this.id.slice(-6) let shortId = timeStr + this.id.slice(-6)
this.inviteCode = convert({numStr: shortId, base: 16, to: 52, alphabet}) this.inviteCode = convert({ numStr: shortId, base: 16, to: 52, alphabet })
} }
}) })
class ActivityUserClass extends BaseModule { class ActivityUserClass extends BaseModule {
@ -89,7 +101,7 @@ class ActivityUserClass extends BaseModule {
@prop() @prop()
public discordName?: string public discordName?: string
@prop({default: 1}) @prop({ default: 1 })
public boost: number public boost: number
/** /**
* boost过期时间 * boost过期时间
@ -104,10 +116,13 @@ class ActivityUserClass extends BaseModule {
@prop({ type: () => [TaskStatus], default: [] }) @prop({ type: () => [TaskStatus], default: [] })
public taskProgress?: TaskStatus[] public taskProgress?: TaskStatus[]
public static async findByCode(this: ReturnModelType<typeof ActivityUserClass>, inviteCode: string, activity: string) { public static async findByCode(
this: ReturnModelType<typeof ActivityUserClass>,
inviteCode: string,
activity: string,
) {
return this.findOne({ inviteCode, activity }).exec() return this.findOne({ inviteCode, activity }).exec()
} }
} }
export const ActivityUser = getModelForClass(ActivityUserClass, { existingConnection: ActivityUserClass.db }) export const ActivityUser = getModelForClass(ActivityUserClass, { existingConnection: ActivityUserClass.db })

View File

@ -102,7 +102,7 @@ export abstract class BaseModule extends FindOrCreate {
* @return {{opt: any, sort: {_id: number}}} * @return {{opt: any, sort: {_id: number}}}
*/ */
public static parseQueryParam(params: {}, options?: any) { public static parseQueryParam(params: {}, options?: any) {
const opt: any = { deleted: { $ne: true } } const opt: any = { deleted: { $ne: true } }
// @ts-ignore // @ts-ignore
let obj = this.schema.paths let obj = this.schema.paths
for (let key in params) { for (let key in params) {
@ -201,6 +201,6 @@ export abstract class BaseModule extends FindOrCreate {
public getTimestampOfID() { public getTimestampOfID() {
// Extract the timestamp from the ObjectId // Extract the timestamp from the ObjectId
// @ts-ignore // @ts-ignore
return this._id.getTimestamp(); return this._id.getTimestamp()
} }
} }

View File

@ -8,7 +8,10 @@ import { BaseModule } from './Base'
*/ */
@dbconn() @dbconn()
@index({ user: 1, activity: 1, wallet: 1 }, { unique: false }) @index({ user: 1, activity: 1, wallet: 1 }, { unique: false })
@modelOptions({ schemaOptions: { collection: 'user_login_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'user_login_record', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class LoginRecordClass extends BaseModule { class LoginRecordClass extends BaseModule {
@prop() @prop()
public user: string public user: string

View File

@ -7,9 +7,12 @@ import { BaseModule } from './Base'
* *
*/ */
@dbconn() @dbconn()
@index({ user: 1, activity: 1}, { unique: false }) @index({ user: 1, activity: 1 }, { unique: false })
@index({ user: 1, activity: 1, dateTag: 1}, { unique: false }) @index({ user: 1, activity: 1, dateTag: 1 }, { unique: false })
@modelOptions({ schemaOptions: { collection: 'lottery_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'lottery_record', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class LotteryRecordClass extends BaseModule { class LotteryRecordClass extends BaseModule {
@prop() @prop()
public user: string public user: string
@ -19,15 +22,15 @@ class LotteryRecordClass extends BaseModule {
public dateTag: string public dateTag: string
@prop() @prop()
public reward?: string public reward?: string
@prop({default: 0}) @prop({ default: 0 })
public amount: number public amount: number
public toJson() { public toJson() {
return { return {
day: this.dateTag, day: this.dateTag,
id: this.reward, id: this.reward,
amount: this.amount, amount: this.amount,
} }
} }
} }
export const LotteryRecord = getModelForClass(LotteryRecordClass, { existingConnection: LotteryRecordClass['db'] }) export const LotteryRecord = getModelForClass(LotteryRecordClass, { existingConnection: LotteryRecordClass['db'] })

View File

@ -7,35 +7,37 @@ import { BaseModule } from './Base'
* *
*/ */
@dbconn() @dbconn()
@index({ user: 1, activity: 1, dateTag: 1}, { unique: false }) @index({ user: 1, activity: 1, dateTag: 1 }, { unique: false })
@modelOptions({ schemaOptions: { collection: 'lottery_stats', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'lottery_stats', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class LotteryStatsClass extends BaseModule { class LotteryStatsClass extends BaseModule {
@prop() @prop()
public user: string public user: string
@prop() @prop()
public activity: string public activity: string
@prop({default: 0}) @prop({ default: 0 })
public amount: number public amount: number
@prop({default: 0}) @prop({ default: 0 })
public daily: number public daily: number
@prop({default: 0}) @prop({ default: 0 })
public gacha: number public gacha: number
@prop({default: 0 }) @prop({ default: 0 })
public share: number public share: number
@prop({default: 0}) @prop({ default: 0 })
public used: number public used: number
@prop() @prop()
public dateTag: string public dateTag: string
public toJson() { public toJson() {
return { return {
amount: this.amount, amount: this.amount,
daily: this.daily, daily: this.daily,
gacha: this.gacha, gacha: this.gacha,
used: this.used, used: this.used,
day: this.dateTag, day: this.dateTag,
} }
} }
} }
export const LotteryStats = getModelForClass(LotteryStatsClass, { existingConnection: LotteryStatsClass['db'] }) export const LotteryStats = getModelForClass(LotteryStatsClass, { existingConnection: LotteryStatsClass['db'] })

View File

@ -2,17 +2,19 @@ import { Severity, getModelForClass, index, modelOptions, mongoose, prop } from
import { dbconn } from 'decorators/dbconn' import { dbconn } from 'decorators/dbconn'
import { BaseModule } from './Base' import { BaseModule } from './Base'
@dbconn() @dbconn()
@index({ user: 1, chain: 1, address: 1}, { unique: false }) @index({ user: 1, chain: 1, address: 1 }, { unique: false })
@index({ user: 1, chain: 1, address: 1, tokenId: 1 }, { unique: true }) @index({ user: 1, chain: 1, address: 1, tokenId: 1 }, { unique: true })
@modelOptions({ schemaOptions: { collection: 'nft_burn_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'nft_burn_record', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class NftBurnRecordClass extends BaseModule { class NftBurnRecordClass extends BaseModule {
@prop({ required: true}) @prop({ required: true })
public user: string public user: string
@prop() @prop()
public chain: number public chain: number
@prop({ required: true}) @prop({ required: true })
public address: string public address: string
@prop() @prop()
public tokenId: string public tokenId: string
@ -20,8 +22,6 @@ class NftBurnRecordClass extends BaseModule {
public activity: string public activity: string
@prop() @prop()
public task: string public task: string
} }
export const NftBurnRecord = getModelForClass(NftBurnRecordClass, { existingConnection: NftBurnRecordClass['db'] }) export const NftBurnRecord = getModelForClass(NftBurnRecordClass, { existingConnection: NftBurnRecordClass['db'] })

View File

@ -21,4 +21,3 @@ class NonceRecordClass extends BaseModule {
} }
export const NonceRecord = getModelForClass(NonceRecordClass, { existingConnection: NonceRecordClass['db'] }) export const NonceRecord = getModelForClass(NonceRecordClass, { existingConnection: NonceRecordClass['db'] })

View File

@ -2,17 +2,19 @@ import { Severity, getModelForClass, index, modelOptions, mongoose, prop } from
import { dbconn } from 'decorators/dbconn' import { dbconn } from 'decorators/dbconn'
import { BaseModule } from './Base' import { BaseModule } from './Base'
@dbconn() @dbconn()
@index({ user: 1 }, { unique: false }) @index({ user: 1 }, { unique: false })
@index({ activity: 1 }, { unique: false }) @index({ activity: 1 }, { unique: false })
@index({user: 1, activity: 1, type: 1}, { unique: false }) @index({ user: 1, activity: 1, type: 1 }, { unique: false })
@modelOptions({ schemaOptions: { collection: 'score_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'score_record', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class ScoreRecordClass extends BaseModule { class ScoreRecordClass extends BaseModule {
@prop({ required: true}) @prop({ required: true })
public user: string public user: string
@prop({ required: true}) @prop({ required: true })
public activity: string public activity: string
@prop() @prop()
@ -23,8 +25,6 @@ class ScoreRecordClass extends BaseModule {
@prop({ type: mongoose.Schema.Types.Mixed }) @prop({ type: mongoose.Schema.Types.Mixed })
public data: any public data: any
} }
export const ScoreRecord = getModelForClass(ScoreRecordClass, { existingConnection: ScoreRecordClass['db'] }) export const ScoreRecord = getModelForClass(ScoreRecordClass, { existingConnection: ScoreRecordClass['db'] })

View File

@ -2,17 +2,19 @@ import { Severity, getModelForClass, index, modelOptions, mongoose, prop } from
import { dbconn } from 'decorators/dbconn' import { dbconn } from 'decorators/dbconn'
import { BaseModule } from './Base' import { BaseModule } from './Base'
@dbconn() @dbconn()
@index({ user: 1 }, { unique: false }) @index({ user: 1 }, { unique: false })
@index({ activity: 1 }, { unique: false }) @index({ activity: 1 }, { unique: false })
@index({user: 1, activity: 1, type: 1}, { unique: false }) @index({ user: 1, activity: 1, type: 1 }, { unique: false })
@modelOptions({ schemaOptions: { collection: 'token_claim_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @modelOptions({
schemaOptions: { collection: 'token_claim_record', timestamps: true },
options: { allowMixed: Severity.ALLOW },
})
class TokenClaimHistoryClass extends BaseModule { class TokenClaimHistoryClass extends BaseModule {
@prop({ required: true}) @prop({ required: true })
public user: string public user: string
@prop({ required: true}) @prop({ required: true })
public activity: string public activity: string
/** /**
* , 便 * , 便
@ -31,7 +33,7 @@ class TokenClaimHistoryClass extends BaseModule {
* 1: 已确认 * 1: 已确认
* -1: 已明确失败 * -1: 已明确失败
*/ */
@prop({default: 0}) @prop({ default: 0 })
public status: number public status: number
// 转账交易hash // 转账交易hash
@prop() @prop()
@ -44,5 +46,6 @@ class TokenClaimHistoryClass extends BaseModule {
} }
} }
export const TokenClaimHistory = getModelForClass(TokenClaimHistoryClass, { existingConnection: TokenClaimHistoryClass['db'] }) export const TokenClaimHistory = getModelForClass(TokenClaimHistoryClass, {
existingConnection: TokenClaimHistoryClass['db'],
})

View File

@ -5,8 +5,8 @@ import { formatDate, yesterday } from 'utils/date.util'
@dbconn('chain') @dbconn('chain')
@index({ from: 1 }, { unique: false }) @index({ from: 1 }, { unique: false })
@index({ from: 1, dateTag: 1}, { unique: true }) @index({ from: 1, dateTag: 1 }, { unique: true })
@index({ from: 1, blockTime: 1}, { unique: false }) @index({ from: 1, blockTime: 1 }, { unique: false })
@modelOptions({ @modelOptions({
schemaOptions: { collection: 'check_in_event', timestamps: true }, schemaOptions: { collection: 'check_in_event', timestamps: true },
}) })
@ -26,7 +26,7 @@ export class CheckInClass extends BaseModule {
@prop() @prop()
public dateTag: string public dateTag: string
// 连签天数 // 连签天数
@prop({default: 0}) @prop({ default: 0 })
public count: number public count: number
@prop() @prop()
public value: string public value: string
@ -34,7 +34,7 @@ export class CheckInClass extends BaseModule {
public input: string public input: string
public static async saveEvent(event: any) { public static async saveEvent(event: any) {
const preDay = formatDate(yesterday()); const preDay = formatDate(yesterday())
const preDayEvent = await CheckIn.findOne({ from: event.from, dateTag: preDay }) const preDayEvent = await CheckIn.findOne({ from: event.from, dateTag: preDay })
if (preDayEvent) { if (preDayEvent) {
event.count = preDayEvent.count + 1 event.count = preDayEvent.count + 1
@ -43,12 +43,12 @@ export class CheckInClass extends BaseModule {
} }
public toJson() { public toJson() {
return { return {
address: this.from, address: this.from,
day: this.dateTag, day: this.dateTag,
time: this.blockTime, time: this.blockTime,
count: this.count count: this.count,
} }
} }
} }

View File

@ -20,15 +20,14 @@ export class NftHolderClass extends BaseModule {
public blockNumber: number public blockNumber: number
@prop() @prop()
public user: string public user: string
@prop({default: false}) @prop({ default: false })
public burn: boolean public burn: boolean
public static async saveData(event: any) { public static async saveData(event: any) {
const address = event.address; const address = event.address
const chain = event.chain; const chain = event.chain
const tokenId = event.tokenId; const tokenId = event.tokenId
const blockNumer = event.blockNumber; const blockNumer = event.blockNumber
const burn = event.to === ZERO_ADDRESS const burn = event.to === ZERO_ADDRESS
let record = await NftHolder.findOne({ address, chain, tokenId }) let record = await NftHolder.findOne({ address, chain, tokenId })
@ -44,8 +43,7 @@ export class NftHolderClass extends BaseModule {
record.blockNumber = blockNumer record.blockNumber = blockNumer
} }
} }
await record.save(); await record.save()
} }
} }

View File

@ -2,10 +2,9 @@ import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoos
import { dbconn } from 'decorators/dbconn' import { dbconn } from 'decorators/dbconn'
import { BaseModule } from '../Base' import { BaseModule } from '../Base'
@dbconn('chain') @dbconn('chain')
@index({ chain: 1, nft: 1, tokenId: 1, start: 1 }, { unique: true }) @index({ chain: 1, nft: 1, tokenId: 1, start: 1 }, { unique: true })
@index({ chain:1, user: 1, nft: 1}, {unique: false}) @index({ chain: 1, user: 1, nft: 1 }, { unique: false })
@modelOptions({ @modelOptions({
schemaOptions: { collection: 'nft_stake_info', timestamps: true }, schemaOptions: { collection: 'nft_stake_info', timestamps: true },
}) })
@ -38,13 +37,13 @@ export class NftStakeClass extends BaseModule {
public version: number public version: number
public toJson() { public toJson() {
return { return {
nft: this.nft, nft: this.nft,
nftid: this.tokenId, nftid: this.tokenId,
start: this.start, start: this.start,
cd: +process.env.STAKE_CD, cd: +process.env.STAKE_CD,
stakeTime: this.stakeTime stakeTime: this.stakeTime,
} }
} }
} }

View File

@ -1,7 +1,6 @@
import { LotteryCache } from 'common/LotteryCache'; import { LotteryCache } from 'common/LotteryCache'
import { singleton } from 'decorators/singleton' import { singleton } from 'decorators/singleton'
import logger from 'logger/logger'; import logger from 'logger/logger'
import {NonceRecord} from 'models/NonceRecord'
import * as schedule from 'node-schedule' import * as schedule from 'node-schedule'
/** /**
@ -9,14 +8,12 @@ import * as schedule from 'node-schedule'
*/ */
@singleton @singleton
export default class CacheSchedule { export default class CacheSchedule {
async updateCache() { async updateCache() {
try { try {
new LotteryCache().flush(); new LotteryCache().flush()
} catch (err) { } catch (err) {
logger.warn(err) logger.warn(err)
} }
} }
scheduleAll() { scheduleAll() {
schedule.scheduleJob('*/10 * * * * *', async () => { schedule.scheduleJob('*/10 * * * * *', async () => {

View File

@ -1,5 +1,5 @@
import { singleton } from 'decorators/singleton' import { singleton } from 'decorators/singleton'
import {NonceRecord} from 'models/NonceRecord' import { NonceRecord } from 'models/NonceRecord'
import * as schedule from 'node-schedule' import * as schedule from 'node-schedule'
/** /**
@ -8,11 +8,11 @@ import * as schedule from 'node-schedule'
@singleton @singleton
export default class NonceRecordSchedule { export default class NonceRecordSchedule {
async parseAllFinishedRecord() { async parseAllFinishedRecord() {
await NonceRecord.deleteMany({status: 1}); await NonceRecord.deleteMany({ status: 1 })
} }
async parseAllExpiredRecord() { async parseAllExpiredRecord() {
let now = Date.now() let now = Date.now()
await NonceRecord.deleteMany({expired: {$lt: now}}) await NonceRecord.deleteMany({ expired: { $lt: now } })
} }
scheduleAll() { scheduleAll() {
schedule.scheduleJob('*/1 * * * *', async () => { schedule.scheduleJob('*/1 * * * *', async () => {

View File

@ -1,53 +1,53 @@
import { NftHolder } from "models/chain/NftHolder" import { NftHolder } from 'models/chain/NftHolder'
import { NftStake } from "models/chain/NftStake" import { NftStake } from 'models/chain/NftStake'
export const queryCheckInList = async (address: string, days: string | number | string[], limit: number = 0) => { export const queryCheckInList = async (address: string, days: string | number | string[], limit: number = 0) => {
const url = process.env.CHAIN_SVR + '/task/check_in' const url = process.env.CHAIN_SVR + '/task/check_in'
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',
headers: {"Content-Type": "application/json"}, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
address, address,
days, days,
limit limit,
}) }),
}).then((res) => res.json()) }).then(res => res.json())
} }
export const queryCheckInSeq = async (address: string) =>{ export const queryCheckInSeq = async (address: string) => {
const url = process.env.CHAIN_SVR + '/task/check_in/max_seq' const url = process.env.CHAIN_SVR + '/task/check_in/max_seq'
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',
headers: {"Content-Type": "application/json"}, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
address, address,
}) }),
}).then((res) => res.json()) }).then(res => res.json())
} }
export const queryBurnNftList = async (address: string, user: string, chain: number) => { export const queryBurnNftList = async (address: string, user: string, chain: number) => {
const url = process.env.CHAIN_SVR + '/task/nft/checkburn' const url = process.env.CHAIN_SVR + '/task/nft/checkburn'
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',
headers: {"Content-Type": "application/json"}, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
address, address,
user, user,
chain chain,
}) }),
}).then((res) => res.json()) }).then(res => res.json())
} }
export const checkHadGacha = async (user: string) => { export const checkHadGacha = async (user: string) => {
const chain = process.env.CHAIN+'' const chain = process.env.CHAIN + ''
const address = process.env.GACHA_CONTRACT const address = process.env.GACHA_CONTRACT
const record = await NftHolder.findOne({user, chain, address}) const record = await NftHolder.findOne({ user, chain, address })
return !!record return !!record
} }
export const queryStakeList = async (userAddress: string) => { export const queryStakeList = async (userAddress: string) => {
const chain = process.env.CHAIN+'' const chain = process.env.CHAIN + ''
const address = process.env.BADGE_CONTRACT const address = process.env.BADGE_CONTRACT
let records = await NftStake.find({chain, nft: address, user: userAddress.toLowerCase()}) let records = await NftStake.find({ chain, nft: address, user: userAddress.toLowerCase() })
return records return records
} }

View File

@ -1,4 +1,4 @@
import { hmacSha256 } from "utils/security.util" import { hmacSha256 } from 'utils/security.util'
import axios from 'axios' import axios from 'axios'
const SECRET_KEY = process.env.HASH_SALT const SECRET_KEY = process.env.HASH_SALT
@ -10,14 +10,13 @@ function createSign(address: string) {
} }
export function checkTwitter(address: string) { export function checkTwitter(address: string) {
let sign = createSign(address); let sign = createSign(address)
const url = `${process.env.OAUTH_SVR_URL}/activity/twitter/${address}?sign=${sign}` const url = `${process.env.OAUTH_SVR_URL}/activity/twitter/${address}?sign=${sign}`
return axios.get(url); return axios.get(url)
} }
export function checkDiscord(address: string) { export function checkDiscord(address: string) {
let sign = createSign(address); let sign = createSign(address)
const url = `${process.env.OAUTH_SVR_URL}/activity/discord/${address}?sign=${sign}`; const url = `${process.env.OAUTH_SVR_URL}/activity/discord/${address}?sign=${sign}`
return axios.get(url); return axios.get(url)
} }

View File

@ -1,6 +1,6 @@
import { ScoreRecord } from "models/ScoreRecord"; import { ScoreRecord } from 'models/ScoreRecord'
import { RedisClient } from "redis/RedisClient"; import { RedisClient } from 'redis/RedisClient'
import { formatDate } from "utils/date.util"; import { formatDate } from 'utils/date.util'
/** /**
* *
@ -16,12 +16,12 @@ export const updateRankScore = async ({
score, score,
activity, activity,
scoreType, scoreType,
scoreParams scoreParams,
}: { }: {
user: string, user: string
score: number, score: number
activity: string, activity: string
scoreType: string, scoreType: string
scoreParams: any scoreParams: any
}) => { }) => {
let record = new ScoreRecord({ let record = new ScoreRecord({
@ -29,14 +29,14 @@ export const updateRankScore = async ({
activity: activity, activity: activity,
score, score,
type: scoreType, type: scoreType,
data: scoreParams data: scoreParams,
}) })
await record.save(); await record.save()
const key = rankKey(activity); const key = rankKey(activity)
await updateRank(key, score, user); await updateRank(key, score, user)
// add daily score // add daily score
const dailyKey = rankKey(activity, new Date()); const dailyKey = rankKey(activity, new Date())
await updateRank(dailyKey, score, user); await updateRank(dailyKey, score, user)
} }
/** /**
@ -46,20 +46,20 @@ export const updateRankScore = async ({
* @param member * @param member
*/ */
const updateRank = async (key: string, score: number, member: string) => { const updateRank = async (key: string, score: number, member: string) => {
let scoreSaved = await new RedisClient().zscore(key, member) + ''; let scoreSaved = (await new RedisClient().zscore(key, member)) + ''
if (scoreSaved) { if (scoreSaved) {
scoreSaved = scoreSaved.substring(0, scoreSaved.indexOf('.')) scoreSaved = scoreSaved.substring(0, scoreSaved.indexOf('.'))
} }
let scoreOld = parseInt(scoreSaved || '0'); let scoreOld = parseInt(scoreSaved || '0')
score = score + scoreOld; score = score + scoreOld
const scoreToSave = score + 1 - (Date.now() / 1000 / 10000000000) const scoreToSave = score + 1 - Date.now() / 1000 / 10000000000
await new RedisClient().zadd(key, scoreToSave, member); await new RedisClient().zadd(key, scoreToSave, member)
} }
export const rankKey = (activity: string, date?: Date) => { export const rankKey = (activity: string, date?: Date) => {
if (!date) { if (!date) {
return `${activity}:score` return `${activity}:score`
} }
const dateTag = formatDate(date); const dateTag = formatDate(date)
return `${activity}:score:${dateTag}` return `${activity}:score:${dateTag}`
} }

View File

@ -1,9 +1,9 @@
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
import { queryBurnNftList } from "services/chain.svr"; import { queryBurnNftList } from 'services/chain.svr'
import { NftBurnRecord } from "models/NFTBrunRecord"; import { NftBurnRecord } from 'models/NFTBrunRecord'
export default class BurnNft extends ITask { export default class BurnNft extends ITask {
static desc = 'Butn NFT' static desc = 'Butn NFT'
@ -23,9 +23,9 @@ export default class BurnNft extends ITask {
chain, chain,
address, address,
}) })
const localNftSet = new Set(); const localNftSet = new Set()
let finishAmount = 0; let finishAmount = 0
let tmpNftSet = new Set(); let tmpNftSet = new Set()
for (let nft of localNft) { for (let nft of localNft) {
localNftSet.add(nft.tokenId) localNftSet.add(nft.tokenId)
if (nft.activity === this.activity.id && nft.task === task.id) { if (nft.activity === this.activity.id && nft.task === task.id) {
@ -33,13 +33,13 @@ export default class BurnNft extends ITask {
tmpNftSet.add(nft.tokenId) tmpNftSet.add(nft.tokenId)
} }
} }
let finishNfts = new Set(); let finishNfts = new Set()
for (let nft of nftList) { for (let nft of nftList) {
if (!localNftSet.has(nft.tokenId)) { if (!localNftSet.has(nft.tokenId)) {
finishNfts.add(nft.tokenId) finishNfts.add(nft.tokenId)
finishAmount += 1 finishAmount += 1
if (finishAmount >= cfg.repeat) { if (finishAmount >= cfg.repeat) {
break; break
} }
} }
} }
@ -50,7 +50,7 @@ export default class BurnNft extends ITask {
address, address,
tokenId: finishNft, tokenId: finishNft,
activity: this.activity.id, activity: this.activity.id,
task: task.id task: task.id,
}) })
await record.save() await record.save()
} }
@ -67,13 +67,13 @@ export default class BurnNft extends ITask {
} }
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'save failed') throw new ZError(100, 'save failed')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }

View File

@ -1,10 +1,10 @@
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskStatus, TaskStatusEnum } from "models/ActivityUser"; import { TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
import { TaskCfg, TaskTypeEnum } from "models/ActivityInfo"; import { TaskCfg, TaskTypeEnum } from 'models/ActivityInfo'
import { queryCheckInList, queryCheckInSeq } from "services/chain.svr"; import { queryCheckInList, queryCheckInSeq } from 'services/chain.svr'
import { updateRankScore } from "services/rank.svr"; import { updateRankScore } from 'services/rank.svr'
import { LotteryCache } from "common/LotteryCache"; import { LotteryCache } from 'common/LotteryCache'
/** /**
* *
@ -22,27 +22,30 @@ export default class DailyCheckIn extends ITask {
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
const days = cfg.params.days || 1 const days = cfg.params.days || 1
const limit = cfg.params.limit || 0 const limit = cfg.params.limit || 0
const res = cfg.type === TaskTypeEnum.DAILY ? const res =
await queryCheckInList(address, days - 1, limit) cfg.type === TaskTypeEnum.DAILY
: await queryCheckInSeq(address) ? await queryCheckInList(address, days - 1, limit)
: await queryCheckInSeq(address)
if (res.errcode) { if (res.errcode) {
throw new ZError(res.errcode, res.errmsg) throw new ZError(res.errcode, res.errmsg)
} }
if ((cfg.type === TaskTypeEnum.DAILY && task.status === TaskStatusEnum.RUNNING && res.data.length >= days) if (
|| (cfg.type === TaskTypeEnum.ONCE && task.status === TaskStatusEnum.RUNNING && res.data.count >= days)) { (cfg.type === TaskTypeEnum.DAILY && task.status === TaskStatusEnum.RUNNING && res.data.length >= days) ||
(cfg.type === TaskTypeEnum.ONCE && task.status === TaskStatusEnum.RUNNING && res.data.count >= days)
) {
task.status = TaskStatusEnum.SUCCESS task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now() task.timeFinish = Date.now()
task.data = res.data task.data = res.data
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'save daily checkin failed') throw new ZError(100, 'save daily checkin failed')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
@ -56,13 +59,13 @@ export default class DailyCheckIn extends ITask {
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
if (cfg.type === TaskTypeEnum.DAILY) { if (cfg.type === TaskTypeEnum.DAILY) {
const res = await queryCheckInList(this.user.address, 1, 0) const res = await queryCheckInList(this.user.address, 1, 0)
const [taskId, dateTag] = task.id.split(':'); const [taskId, dateTag] = task.id.split(':')
let list: { day: string, time: number, count: number }[] = res.data; let list: { day: string; time: number; count: number }[] = res.data
const countCfg = cfg.params.score.length; const countCfg = cfg.params.score.length
let count = list.length > 0 ? list[0].count : 0; let count = list.length > 0 ? list[0].count : 0
let seq = count % countCfg; let seq = count % countCfg
let score = cfg.params.score[seq] || 0 + cfg.score; let score = cfg.params.score[seq] || 0 + cfg.score
const user = this.user const user = this.user
if (user.boost > 1 && Date.now() < user.boostExpire) { if (user.boost > 1 && Date.now() < user.boostExpire) {
score = Math.floor(score * user.boost) score = Math.floor(score * user.boost)
@ -77,18 +80,17 @@ export default class DailyCheckIn extends ITask {
date: dateTag, date: dateTag,
bouns: score, bouns: score,
boost: user.boost, boost: user.boost,
} },
}) })
await this.claimItem(cfg) await this.claimItem(cfg)
} else { } else {
await super.claimReward(task); await super.claimReward(task)
} }
// 更新gacha拥有者抽奖次数, 这里写死, 等想到好的方式再处理 // 更新gacha拥有者抽奖次数, 这里写死, 等想到好的方式再处理
let record = await new LotteryCache().getData(this.user.id, this.user.activity); let record = await new LotteryCache().getData(this.user.id, this.user.activity)
if (record.daily === 0) { if (record.daily === 0) {
record.daily += 1 record.daily += 1
record.amount += 1 record.amount += 1
} }
} }
} }

View File

@ -1,8 +1,8 @@
import { checkDiscord } from "services/oauth.svr"; import { checkDiscord } from 'services/oauth.svr'
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
export default class DiscordConnect extends ITask { export default class DiscordConnect extends ITask {
static desc = 'join discord' static desc = 'join discord'
@ -28,18 +28,17 @@ export default class DiscordConnect extends ITask {
this.user.discordName = res.data.data.username this.user.discordName = res.data.data.username
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'discord already binded') throw new ZError(100, 'discord already binded')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
} }
return true return true
} }
} }

View File

@ -1,7 +1,7 @@
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
export default class DiscordJoin extends ITask { export default class DiscordJoin extends ITask {
static desc = 'join discord' static desc = 'join discord'
@ -10,7 +10,7 @@ export default class DiscordJoin extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let time = cfg.params.time; let time = cfg.params.time
if (Date.now() - task.timeStart < time * 1000) { if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'check discord join failed') throw new ZError(11, 'check discord join failed')
} }
@ -23,17 +23,16 @@ export default class DiscordJoin extends ITask {
task.data = {} task.data = {}
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'already join discord') throw new ZError(100, 'already join discord')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
return true return true
} }
} }

View File

@ -1,7 +1,7 @@
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
export default class DiscordRole extends ITask { export default class DiscordRole extends ITask {
static desc = 'acquire discord role' static desc = 'acquire discord role'
@ -9,7 +9,7 @@ export default class DiscordRole extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let time = cfg.params.time; let time = cfg.params.time
if (Date.now() - task.timeStart < time * 1000) { if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'check discord role failed') throw new ZError(11, 'check discord role failed')
} }
@ -22,17 +22,16 @@ export default class DiscordRole extends ITask {
task.data = {} task.data = {}
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'already acquired discord role') throw new ZError(100, 'already acquired discord role')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
return true return true
} }
} }

View File

@ -1,9 +1,8 @@
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
import { UserLog } from "models/UserLog"; import { LoginRecord } from 'models/LoginRecord'
import { LoginRecord } from "models/LoginRecord";
export default class OkxLogin extends ITask { export default class OkxLogin extends ITask {
static desc = 'okx wallet login' static desc = 'okx wallet login'
@ -13,22 +12,22 @@ export default class OkxLogin extends ITask {
const { task } = data const { task } = data
const { activity } = this.user const { activity } = this.user
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let wallet = 'okx'; let wallet = 'okx'
let record = LoginRecord.findOne({ user: this.user.id, activity, wallet}) let record = LoginRecord.findOne({ user: this.user.id, activity, wallet })
if (!record ) { if (!record) {
throw new ZError(11, 'task not finished') throw new ZError(11, 'task not finished')
} }
task.status = TaskStatusEnum.SUCCESS task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now() task.timeFinish = Date.now()
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'discord already binded') throw new ZError(100, 'discord already binded')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }

View File

@ -1,18 +1,24 @@
import { ActivityUser, TaskStatus, TaskStatusEnum } from "models/ActivityUser"; import { ActivityUser, TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
import { updateRankScore } from "services/rank.svr"; import { updateRankScore } from 'services/rank.svr'
import { ActivityItem } from "models/ActivityItem"; import { ActivityItem } from 'models/ActivityItem'
import { LotteryCache } from "common/LotteryCache"; import { LotteryCache } from 'common/LotteryCache'
import { checkHadGacha } from "services/chain.svr"; import { checkHadGacha } from 'services/chain.svr'
const updateInviteScore = async (user: typeof ActivityUser, scores: number[], items: any[], level: number, reason: string) => { const updateInviteScore = async (
user: typeof ActivityUser,
scores: number[],
items: any[],
level: number,
reason: string,
) => {
if (!user.inviteUser || scores.length <= level) { if (!user.inviteUser || scores.length <= level) {
return; return
} }
let userSup = await ActivityUser.findById(user.inviteUser) let userSup = await ActivityUser.findById(user.inviteUser)
if (!userSup || !userSup.address) { if (!userSup || !userSup.address) {
return; return
} }
await updateRankScore({ await updateRankScore({
user: userSup.id, user: userSup.id,
@ -21,22 +27,21 @@ const updateInviteScore = async (user: typeof ActivityUser, scores: number[], it
scoreType: reason, scoreType: reason,
scoreParams: { scoreParams: {
user: user.id, user: user.id,
level level,
} },
}) })
// 更新gacha拥有者抽奖次数, 这里写死, 等想到好的方式再处理 // 更新gacha拥有者抽奖次数, 这里写死, 等想到好的方式再处理
let record = await new LotteryCache().getData(userSup.id, userSup.activity); let record = await new LotteryCache().getData(userSup.id, userSup.activity)
record.share += 1 record.share += 1
record.amount += 1 record.amount += 1
if (record.gacha ===0 && checkHadGacha(userSup.address)) { if (record.gacha === 0 && checkHadGacha(userSup.address)) {
record.gacha += 1 record.gacha += 1
record.amount += 1 record.amount += 1
} }
if (items.length > level) { if (items.length > level) {
for (let key in items[level]) { for (let key in items[level]) {
let amount = items[level][key] let amount = items[level][key]
await ActivityItem.insertOrUpdate( await ActivityItem.insertOrUpdate({ user: userSup, activity: userSup.activity, item: key }, { $inc: { amount } })
{user: userSup, activity: userSup.activity, item: key}, {$inc: {amount}})
} }
} }
await updateInviteScore(userSup, scores, items, level + 1, reason) await updateInviteScore(userSup, scores, items, level + 1, reason)
@ -52,18 +57,18 @@ export default class ShareCode extends ITask {
if (!this.user.inviteUser) { if (!this.user.inviteUser) {
throw new Error('not finished') throw new Error('not finished')
} }
let scores = cfg.params.score; let scores = cfg.params.score
const items = cfg.params.inviteItems || []; const items = cfg.params.inviteItems || []
task.status = TaskStatusEnum.SUCCESS task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now() task.timeFinish = Date.now()
task.data = {} task.data = {}
await this.user.save(); await this.user.save()
// According to configuration, add score to user who invite current user // According to configuration, add score to user who invite current user
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await super.claimReward(task); await super.claimReward(task)
await updateInviteScore(this.user, scores, items, 0, task.task) await updateInviteScore(this.user, scores, items, 0, task.task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
@ -71,10 +76,10 @@ export default class ShareCode extends ITask {
} }
public async claimReward(task: TaskStatus) { public async claimReward(task: TaskStatus) {
await super.claimReward(task); await super.claimReward(task)
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let scores = cfg.params.score; let scores = cfg.params.score
const items = cfg.params.inviteItems || []; const items = cfg.params.inviteItems || []
await updateInviteScore(this.user, scores, items, 0, "invite") await updateInviteScore(this.user, scores, items, 0, 'invite')
} }
} }

View File

@ -1,8 +1,8 @@
import { checkTwitter } from "services/oauth.svr"; import { checkTwitter } from 'services/oauth.svr'
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { ActivityUser, TaskStatusEnum } from "models/ActivityUser"; import { ActivityUser, TaskStatusEnum } from 'models/ActivityUser'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
export default class TwitterConnect extends ITask { export default class TwitterConnect extends ITask {
static desc = 'twitter connect' static desc = 'twitter connect'
@ -26,20 +26,18 @@ export default class TwitterConnect extends ITask {
this.user.twitterName = res.data.data.username this.user.twitterName = res.data.data.username
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'twitter already binded') throw new ZError(100, 'twitter already binded')
} }
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
} }
return true return true
} }
} }

View File

@ -1,7 +1,7 @@
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
export default class TwitterFollow extends ITask { export default class TwitterFollow extends ITask {
static desc = 'twitter follow' static desc = 'twitter follow'
@ -9,7 +9,7 @@ export default class TwitterFollow extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let time = cfg.params.time; let time = cfg.params.time
if (Date.now() - task.timeStart < time * 1000) { if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'follow failed') throw new ZError(11, 'follow failed')
} }
@ -22,17 +22,16 @@ export default class TwitterFollow extends ITask {
task.data = {} task.data = {}
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'save failed') throw new ZError(100, 'save failed')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
return true return true
} }
} }

View File

@ -1,6 +1,6 @@
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
export default class TwitterRetweet extends ITask { export default class TwitterRetweet extends ITask {
static desc = 'twitter retweet' static desc = 'twitter retweet'
@ -8,7 +8,7 @@ export default class TwitterRetweet extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.activity.tasks.find(t => t.id === task.id) let cfg = this.activity.tasks.find(t => t.id === task.id)
let time = cfg.params.time; let time = cfg.params.time
if (Date.now() - task.timeStart < time * 1000) { if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'retweet failed') throw new ZError(11, 'retweet failed')
} }
@ -21,17 +21,16 @@ export default class TwitterRetweet extends ITask {
task.data = {} task.data = {}
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'save failed') throw new ZError(100, 'save failed')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
return true return true
} }
} }

View File

@ -1,7 +1,7 @@
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { TaskCfg } from "models/ActivityInfo"; import { TaskCfg } from 'models/ActivityInfo'
export default class YoutubeFollow extends ITask { export default class YoutubeFollow extends ITask {
static desc = 'youtube follow' static desc = 'youtube follow'
@ -9,7 +9,7 @@ export default class YoutubeFollow extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id) let cfg = this.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let time = cfg.params.time; let time = cfg.params.time
if (Date.now() - task.timeStart < time * 1000) { if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'follow failed') throw new ZError(11, 'follow failed')
} }
@ -22,17 +22,16 @@ export default class YoutubeFollow extends ITask {
task.data = {} task.data = {}
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'save failed') throw new ZError(100, 'save failed')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
return true return true
} }
} }

View File

@ -1,6 +1,6 @@
import { ZError } from "common/ZError"; import { ZError } from 'common/ZError'
import { ITask } from "./base/ITask"; import { ITask } from './base/ITask'
import { TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from 'models/ActivityUser'
export default class YoutubePost extends ITask { export default class YoutubePost extends ITask {
static desc = 'youtube post' static desc = 'youtube post'
@ -8,7 +8,7 @@ export default class YoutubePost extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.activity.tasks.find(t => t.id === task.id) let cfg = this.activity.tasks.find(t => t.id === task.id)
let time = cfg.params.time; let time = cfg.params.time
if (Date.now() - task.timeStart < time * 1000) { if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'post failed') throw new ZError(11, 'post failed')
} }
@ -21,17 +21,16 @@ export default class YoutubePost extends ITask {
task.data = {} task.data = {}
try { try {
await this.user.save() await this.user.save()
} catch(err) { } catch (err) {
throw new ZError(100, 'save failed') throw new ZError(100, 'save failed')
} }
if (cfg.autoclaim) { if (cfg.autoclaim) {
try { try {
await this.claimReward(task); await this.claimReward(task)
} catch(err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
return true return true
} }
} }

View File

@ -1,7 +1,7 @@
import { ActivityInfo, TaskCfg } from "models/ActivityInfo" import { ActivityInfo, TaskCfg } from 'models/ActivityInfo'
import { ActivityItem } from "models/ActivityItem" import { ActivityItem } from 'models/ActivityItem'
import { ActivityUser, TaskStatus, TaskStatusEnum } from "models/ActivityUser" import { ActivityUser, TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
import { updateRankScore } from "services/rank.svr" import { updateRankScore } from 'services/rank.svr'
export abstract class ITask { export abstract class ITask {
static desc: string static desc: string
@ -11,8 +11,7 @@ export abstract class ITask {
user: typeof ActivityUser user: typeof ActivityUser
activity: typeof ActivityInfo activity: typeof ActivityInfo
constructor({ user, activity }: { user: typeof ActivityUser; activity: typeof ActivityInfo }) {
constructor({user, activity}: {user: typeof ActivityUser, activity: typeof ActivityInfo}) {
// do nothing // do nothing
this.user = user this.user = user
this.activity = activity this.activity = activity
@ -21,10 +20,10 @@ export abstract class ITask {
public async claimReward(task: any) { public async claimReward(task: any) {
const user = this.user const user = this.user
const [taskId, dateTag] = task.id.split(':'); const [taskId, dateTag] = task.id.split(':')
const cfg = this.activity.tasks.find((t: TaskCfg) => t.id === taskId) const cfg = this.activity.tasks.find((t: TaskCfg) => t.id === taskId)
if (!cfg.score) { if (!cfg.score) {
return; return
} }
let claimAmount = task.data.claimAmount || 0 let claimAmount = task.data.claimAmount || 0
let score = cfg.score let score = cfg.score
@ -41,15 +40,14 @@ export abstract class ITask {
scoreParams: { scoreParams: {
date: dateTag, date: dateTag,
taskId: task.id, taskId: task.id,
boost: user.boost boost: user.boost,
} },
}) })
claimAmount += 1 claimAmount += 1
} }
task.data.claimAmount = claimAmount task.data.claimAmount = claimAmount
task.markModified('data') task.markModified('data')
if ((cfg.repeat > 1 && claimAmount >= cfg.repeat) if ((cfg.repeat > 1 && claimAmount >= cfg.repeat) || (cfg.repeat === 1 && claimAmount >= 1)) {
|| (cfg.repeat === 1 && claimAmount >= 1)) {
task.status = TaskStatusEnum.CLAIMED task.status = TaskStatusEnum.CLAIMED
task.timeClaim = Date.now() task.timeClaim = Date.now()
} }
@ -62,7 +60,9 @@ export abstract class ITask {
for (let key in cfg.params.items) { for (let key in cfg.params.items) {
let amount = cfg.params.items[key] let amount = cfg.params.items[key]
await ActivityItem.insertOrUpdate( await ActivityItem.insertOrUpdate(
{user: this.user.id, activity: this.user.activity, item: key}, {$inc: {amount}, last: Date.now()}) { user: this.user.id, activity: this.user.activity, item: key },
{ $inc: { amount }, last: Date.now() },
)
} }
} }
} }

View File

@ -1,6 +1,6 @@
import { recoverTypedSignature, SignTypedDataVersion } from '@metamask/eth-sig-util' import { recoverTypedSignature, SignTypedDataVersion } from '@metamask/eth-sig-util'
import { soliditySha3, toWei } from 'web3-utils' import { soliditySha3, toWei } from 'web3-utils'
import Web3 from 'web3'; import Web3 from 'web3'
export function recoverTypedSignatureV4(signObj: any, signature: string) { export function recoverTypedSignatureV4(signObj: any, signature: string) {
return recoverTypedSignature({ return recoverTypedSignature({
@ -46,22 +46,29 @@ export function buildLoginSignMsg(nonce: string, tips: string) {
return signObj return signObj
} }
export const sign = async ({
export const sign = async ({ user, token, amount, saltNonce } user,
: {user: string, token: string, amount: number | string, saltNonce?: string}) => { token,
const web3 = new Web3(); amount,
let privateKey = process.env.SIGN_PRIVATE_KEY; saltNonce,
const acc = web3.eth.accounts.privateKeyToAccount(privateKey); }: {
const account = web3.eth.accounts.wallet.add(acc); user: string
token: string
amount: number | string
saltNonce?: string
}) => {
const web3 = new Web3()
let privateKey = process.env.SIGN_PRIVATE_KEY
const acc = web3.eth.accounts.privateKeyToAccount(privateKey)
const account = web3.eth.accounts.wallet.add(acc)
const executor = account.address const executor = account.address
const amountBn = toWei(amount+''); const amountBn = toWei(amount + '')
const chainId = process.env.CHAIN; const chainId = process.env.CHAIN
const claimContract = process.env.CLAIM_CONTRACT; const claimContract = process.env.CLAIM_CONTRACT
const startTime = Date.now() / 1000 | 0 const startTime = (Date.now() / 1000) | 0
saltNonce = saltNonce || ((Math.random() * 1000) | 0) + ''; saltNonce = saltNonce || ((Math.random() * 1000) | 0) + ''
let signStr = soliditySha3.apply(this, let signStr = soliditySha3.apply(this, [user, token, claimContract, chainId, amountBn, startTime, saltNonce])
[user, token, claimContract, chainId, amountBn, startTime, saltNonce]); let signature = await web3.eth.sign(signStr, executor)
let signature = await web3.eth.sign(signStr, executor); signature = signature.replace(/00$/, '1b').replace(/01$/, '1c')
signature = signature.replace(/00$/, "1b").replace(/01$/, "1c"); return { token, amount: amountBn, startTime, saltNonce, signature }
return {token, amount: amountBn, startTime, saltNonce, signature}
} }

View File

@ -1,20 +1,20 @@
// format the date to the format we want // format the date to the format we want
export const formatDate = (date: Date): string => { export const formatDate = (date: Date): string => {
const year = date.getFullYear(); const year = date.getFullYear()
const month = (date.getMonth() + 1 + '').padStart(2, '0'); const month = (date.getMonth() + 1 + '').padStart(2, '0')
const day = (date.getDate() + '').padStart(2, '0'); const day = (date.getDate() + '').padStart(2, '0')
return `${year}${month}${day}`; return `${year}${month}${day}`
}; }
// get formated datestring of yesterday // get formated datestring of yesterday
export const yesterday = (date?: Date) => { export const yesterday = (date?: Date) => {
date = date || new Date(); date = date || new Date()
date.setDate(date.getDate() - 1); date.setDate(date.getDate() - 1)
return date; return date
}; }
export const nextday = (date?: Date) => { export const nextday = (date?: Date) => {
date = date || new Date(); date = date || new Date()
date.setDate(date.getDate() + 1); date.setDate(date.getDate() + 1)
return date; return date
} }

View File

@ -1,4 +1,4 @@
import { ZError } from "common/ZError" import { ZError } from 'common/ZError'
const TIMEOUT_ERROR = new Error('timeout') const TIMEOUT_ERROR = new Error('timeout')
@ -140,7 +140,9 @@ export function generateHeader() {
} }
export const checkParamsNeeded = (...args) => { export const checkParamsNeeded = (...args) => {
args.forEach((arg) => {if (!arg) { args.forEach(arg => {
throw new ZError(10, 'params mismatch'); if (!arg) {
}}); throw new ZError(10, 'params mismatch')
}
})
} }

View File

@ -1,5 +1,3 @@
export const ONE_DAY = 24 * 60 * 60 * 1000 export const ONE_DAY = 24 * 60 * 60 * 1000
export const NFT_BEGIN_DAY = new Date(2023, 4, 8) export const NFT_BEGIN_DAY = new Date(2023, 4, 8)
@ -21,5 +19,3 @@ export function daysBetween(date1: Date, date2: Date) {
const diffInDays = Math.round(diffInMs / ONE_DAY) const diffInDays = Math.round(diffInMs / ONE_DAY)
return diffInDays return diffInDays
} }

View File

@ -1,5 +1,5 @@
import Web3 from 'web3'; import Web3 from 'web3'
import { BN } from 'ethereumjs-util'; import { BN } from 'ethereumjs-util'
/** /**
* Converts some token minimal unit to render format string, showing 5 decimals * Converts some token minimal unit to render format string, showing 5 decimals
@ -10,23 +10,17 @@ import { BN } from 'ethereumjs-util';
* @returns {String} - Number of token minimal unit, in render format * @returns {String} - Number of token minimal unit, in render format
* If value is less than 5 precision decimals will show '< 0.00001' * If value is less than 5 precision decimals will show '< 0.00001'
*/ */
export function renderFromTokenMinimalUnit( export function renderFromTokenMinimalUnit(tokenValue, decimals, decimalsToShow = 5) {
tokenValue, const minimalUnit = fromTokenMinimalUnit(tokenValue || 0, decimals)
decimals, const minimalUnitNumber = parseFloat(minimalUnit)
decimalsToShow = 5 let renderMinimalUnit
) {
const minimalUnit = fromTokenMinimalUnit(tokenValue || 0, decimals);
const minimalUnitNumber = parseFloat(minimalUnit);
let renderMinimalUnit;
if (minimalUnitNumber < 0.00001 && minimalUnitNumber > 0) { if (minimalUnitNumber < 0.00001 && minimalUnitNumber > 0) {
renderMinimalUnit = "< 0.00001"; renderMinimalUnit = '< 0.00001'
} else { } else {
const base = Math.pow(10, decimalsToShow); const base = Math.pow(10, decimalsToShow)
renderMinimalUnit = ( renderMinimalUnit = (Math.round(minimalUnitNumber * base) / base).toString()
Math.round(minimalUnitNumber * base) / base
).toString();
} }
return renderMinimalUnit; return renderMinimalUnit
} }
/** /**
* Converts token minimal unit to readable string value * Converts token minimal unit to readable string value
@ -36,25 +30,25 @@ export function renderFromTokenMinimalUnit(
* @returns {string} - String containing the new number * @returns {string} - String containing the new number
*/ */
export function fromTokenMinimalUnit(minimalInput, decimals) { export function fromTokenMinimalUnit(minimalInput, decimals) {
minimalInput = addHexPrefix(Number(minimalInput).toString(16)); minimalInput = addHexPrefix(Number(minimalInput).toString(16))
let minimal = safeNumberToBN(minimalInput); let minimal = safeNumberToBN(minimalInput)
const negative = minimal.lt(new BN(0)); const negative = minimal.lt(new BN(0))
const base = Web3.utils.toBN(Math.pow(10, decimals).toString()); const base = Web3.utils.toBN(Math.pow(10, decimals).toString())
if (negative) { if (negative) {
minimal = minimal.mul(new BN(-1)); minimal = minimal.mul(new BN(-1))
} }
let fraction = minimal.mod(base).toString(10); let fraction = minimal.mod(base).toString(10)
while (fraction.length < decimals) { while (fraction.length < decimals) {
fraction = "0" + fraction; fraction = '0' + fraction
} }
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1]; fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1]
const whole = minimal.div(base).toString(10); const whole = minimal.div(base).toString(10)
let value = "" + whole + (fraction === "0" ? "" : "." + fraction); let value = '' + whole + (fraction === '0' ? '' : '.' + fraction)
if (negative) { if (negative) {
value = "-" + value; value = '-' + value
} }
return value; return value
} }
/** /**
@ -65,20 +59,20 @@ export function fromTokenMinimalUnit(minimalInput, decimals) {
* @returns {String} - Number of token minimal unit, in render format * @returns {String} - Number of token minimal unit, in render format
* If value is less than 5 precision decimals will show '< 0.00001' * If value is less than 5 precision decimals will show '< 0.00001'
*/ */
export function renderFromWei(value, decimalsToShow = 5) { export function renderFromWei(value, decimalsToShow = 5) {
let renderWei = '0'; let renderWei = '0'
// avoid undefined // avoid undefined
if (value) { if (value) {
const wei = Web3.utils.fromWei(value); const wei = Web3.utils.fromWei(value)
const weiNumber = parseFloat(wei); const weiNumber = parseFloat(wei)
if (weiNumber < 0.00001 && weiNumber > 0) { if (weiNumber < 0.00001 && weiNumber > 0) {
renderWei = '< 0.00001'; renderWei = '< 0.00001'
} else { } else {
const base = Math.pow(10, decimalsToShow); const base = Math.pow(10, decimalsToShow)
renderWei = (Math.round(weiNumber * base) / base).toString(); renderWei = (Math.round(weiNumber * base) / base).toString()
} }
} }
return renderWei; return renderWei
} }
/** /**
@ -89,7 +83,7 @@ export function fromTokenMinimalUnit(minimalInput, decimals) {
* @returns {string} - String of the hex token value * @returns {string} - String of the hex token value
*/ */
export function calcTokenValueToSend(value, decimals) { export function calcTokenValueToSend(value, decimals) {
return value ? (value * Math.pow(10, decimals)).toString(16) : 0; return value ? (value * Math.pow(10, decimals)).toString(16) : 0
} }
/** /**
@ -99,11 +93,7 @@ export function calcTokenValueToSend(value, decimals) {
* @returns {boolean} - True if the string is a valid decimal * @returns {boolean} - True if the string is a valid decimal
*/ */
export function isDecimal(value) { export function isDecimal(value) {
return ( return Number.isFinite(parseFloat(value)) && !Number.isNaN(parseFloat(value)) && !isNaN(+value)
Number.isFinite(parseFloat(value)) &&
!Number.isNaN(parseFloat(value)) &&
!isNaN(+value)
);
} }
/** /**
@ -113,7 +103,7 @@ export function isDecimal(value) {
* @returns {Object} - BN instance * @returns {Object} - BN instance
*/ */
export function toBN(value) { export function toBN(value) {
return Web3.utils.toBN(value); return Web3.utils.toBN(value)
} }
/** /**
@ -123,20 +113,20 @@ export function toBN(value) {
* @returns {string} The prefixed string. * @returns {string} The prefixed string.
*/ */
export const addHexPrefix = (str: string) => { export const addHexPrefix = (str: string) => {
if (typeof str !== "string" || str.match(/^-?0x/u)) { if (typeof str !== 'string' || str.match(/^-?0x/u)) {
return str; return str
} }
if (str.match(/^-?0X/u)) { if (str.match(/^-?0X/u)) {
return str.replace("0X", "0x"); return str.replace('0X', '0x')
} }
if (str.startsWith("-")) { if (str.startsWith('-')) {
return str.replace("-", "-0x"); return str.replace('-', '-0x')
} }
return `0x${str}`; return `0x${str}`
}; }
/** /**
* Wraps 'numberToBN' method to avoid potential undefined and decimal values * Wraps 'numberToBN' method to avoid potential undefined and decimal values
@ -145,8 +135,8 @@ export const addHexPrefix = (str: string) => {
* @returns {Object} - The converted value as BN instance * @returns {Object} - The converted value as BN instance
*/ */
export function safeNumberToBN(value: number | string) { export function safeNumberToBN(value: number | string) {
const safeValue = fastSplit(value.toString()) || "0"; const safeValue = fastSplit(value.toString()) || '0'
return numberToBN(safeValue); return numberToBN(safeValue)
} }
/** /**
@ -157,66 +147,58 @@ export function safeNumberToBN(value: number | string) {
* @returns {string} - the selected splitted element * @returns {string} - the selected splitted element
*/ */
export function fastSplit(value, divider = ".") { export function fastSplit(value, divider = '.') {
value += ""; value += ''
const [from, to] = [value.indexOf(divider), 0]; const [from, to] = [value.indexOf(divider), 0]
return value.substring(from, to) || value; return value.substring(from, to) || value
} }
export function stripHexPrefix(str: string) { export function stripHexPrefix(str: string) {
if (typeof str !== "string") { if (typeof str !== 'string') {
return str; return str
} }
return str.slice(0, 2) === "0x" ? str.slice(2) : str; return str.slice(0, 2) === '0x' ? str.slice(2) : str
} }
export function numberToBN(arg) { export function numberToBN(arg) {
if (typeof arg === "string" || typeof arg === "number") { if (typeof arg === 'string' || typeof arg === 'number') {
var multiplier = Web3.utils.toBN(1); // eslint-disable-line var multiplier = Web3.utils.toBN(1); // eslint-disable-line
var formattedString = String(arg).toLowerCase().trim(); var formattedString = String(arg).toLowerCase().trim()
var isHexPrefixed = var isHexPrefixed = formattedString.substr(0, 2) === '0x' || formattedString.substr(0, 3) === '-0x'
formattedString.substr(0, 2) === "0x" ||
formattedString.substr(0, 3) === "-0x";
var stringArg = stripHexPrefix(formattedString); // eslint-disable-line var stringArg = stripHexPrefix(formattedString); // eslint-disable-line
if (stringArg.substr(0, 1) === "-") { if (stringArg.substr(0, 1) === '-') {
stringArg = stripHexPrefix(stringArg.slice(1)); stringArg = stripHexPrefix(stringArg.slice(1))
multiplier = Web3.utils.toBN(-1); multiplier = Web3.utils.toBN(-1)
} }
stringArg = stringArg === "" ? "0" : stringArg; stringArg = stringArg === '' ? '0' : stringArg
if ( if (
(!stringArg.match(/^-?[0-9]+$/) && stringArg.match(/^[0-9A-Fa-f]+$/)) || (!stringArg.match(/^-?[0-9]+$/) && stringArg.match(/^[0-9A-Fa-f]+$/)) ||
stringArg.match(/^[a-fA-F]+$/) || stringArg.match(/^[a-fA-F]+$/) ||
(isHexPrefixed === true && stringArg.match(/^[0-9A-Fa-f]+$/)) (isHexPrefixed === true && stringArg.match(/^[0-9A-Fa-f]+$/))
) { ) {
return Web3.utils.toBN(stringArg).mul(multiplier); return Web3.utils.toBN(stringArg).mul(multiplier)
} }
if ( if ((stringArg.match(/^-?[0-9]+$/) || stringArg === '') && isHexPrefixed === false) {
(stringArg.match(/^-?[0-9]+$/) || stringArg === "") && return Web3.utils.toBN(stringArg).mul(multiplier)
isHexPrefixed === false
) {
return Web3.utils.toBN(stringArg).mul(multiplier);
} }
} else if (typeof arg === "object" && arg.toString && !arg.pop && !arg.push) { } else if (typeof arg === 'object' && arg.toString && !arg.pop && !arg.push) {
if ( if (arg.toString(10).match(/^-?[0-9]+$/) && (arg.mul || arg.dividedToIntegerBy)) {
arg.toString(10).match(/^-?[0-9]+$/) && return Web3.utils.toBN(arg.toString(10))
(arg.mul || arg.dividedToIntegerBy)
) {
return Web3.utils.toBN(arg.toString(10));
} }
} }
throw new Error( throw new Error(
"[number-to-bn] while converting number " + '[number-to-bn] while converting number ' +
JSON.stringify(arg) + JSON.stringify(arg) +
" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported." ' to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported.',
); )
} }
function checkRadixLegal(radix) { function checkRadixLegal(radix) {
return radix >= 2 && radix <= 62; return radix >= 2 && radix <= 62
} }
/** /**
@ -226,18 +208,18 @@ function checkRadixLegal(radix) {
*/ */
function transformCharToNum(letter, base) { function transformCharToNum(letter, base) {
if (base <= 36) { if (base <= 36) {
letter = letter.toLowerCase(); letter = letter.toLowerCase()
} }
if (letter >= '0' && letter <= '9') { if (letter >= '0' && letter <= '9') {
return parseInt(letter); return parseInt(letter)
} }
if (letter >= 'a' && letter <= 'z') { if (letter >= 'a' && letter <= 'z') {
return letter.charCodeAt(0) - 'a'.charCodeAt(0) + 10; return letter.charCodeAt(0) - 'a'.charCodeAt(0) + 10
} }
if (letter >= 'A' && letter <= 'Z') { if (letter >= 'A' && letter <= 'Z') {
return letter.charCodeAt(0) - 'A'.charCodeAt(0) + 36; return letter.charCodeAt(0) - 'A'.charCodeAt(0) + 36
} }
return 0; return 0
} }
/** /**
@ -246,8 +228,8 @@ function transformCharToNum(letter, base) {
* @return {string} * @return {string}
*/ */
function transformNumToChar(num, alphabet) { function transformNumToChar(num, alphabet) {
alphabet = alphabet || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; alphabet = alphabet || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
return alphabet.charAt(num); return alphabet.charAt(num)
} }
/** /**
@ -257,30 +239,40 @@ function transformNumToChar(num, alphabet) {
* @param {number} to * @param {number} to
* @return {string} * @return {string}
*/ */
export function convert({numStr, base, to, alphabet}: {numStr: string, base: number, to: number, alphabet?: string}): string { export function convert({
numStr,
base,
to,
alphabet,
}: {
numStr: string
base: number
to: number
alphabet?: string
}): string {
// 当base和to相等 或 base和to超出转换范围则原样返回 // 当base和to相等 或 base和to超出转换范围则原样返回
if (base === to || !checkRadixLegal(base) || !checkRadixLegal(to)) { if (base === to || !checkRadixLegal(base) || !checkRadixLegal(to)) {
return numStr; return numStr
} }
// 先转成10进制 // 先转成10进制
let p = 0; let p = 0
let number10 = 0; let number10 = 0
while (p < numStr.length) { while (p < numStr.length) {
number10 *= base; number10 *= base
number10 += transformCharToNum(numStr.charAt(p), base); number10 += transformCharToNum(numStr.charAt(p), base)
p++; p++
} }
// 若要转换的正好是进制,则直接返回 // 若要转换的正好是进制,则直接返回
if (to === 10) { if (to === 10) {
return number10.toString(); return number10.toString()
} }
let result = ''; let result = ''
let cur; let cur
while (number10) { while (number10) {
cur = number10 % to; cur = number10 % to
result = transformNumToChar(cur, alphabet) + result; result = transformNumToChar(cur, alphabet) + result
number10 = Math.floor(number10 / to); number10 = Math.floor(number10 / to)
} }
return result; return result
} }

View File

@ -82,73 +82,72 @@ export function checkSign({
// } // }
export const aesEncrypt = (plaintText, key) => { export const aesEncrypt = (plaintText, key) => {
key = CryptoJS.SHA1(key).toString().substring(0,16) key = CryptoJS.SHA1(key).toString().substring(0, 16)
key = CryptoJS.enc.Base64.parse(key) key = CryptoJS.enc.Base64.parse(key)
let encryptedData = CryptoJS.AES.encrypt(plaintText, key, { let encryptedData = CryptoJS.AES.encrypt(plaintText, key, {
mode: CryptoJS.mode.ECB, mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7 padding: CryptoJS.pad.Pkcs7,
}); })
return encryptedData.toString(CryptoJS.format.Hex); return encryptedData.toString(CryptoJS.format.Hex)
} }
export const aesDecrypt = (encryptedDataHexStr, key) => { export const aesDecrypt = (encryptedDataHexStr, key) => {
key = CryptoJS.SHA1(key).toString().substring(0,16) key = CryptoJS.SHA1(key).toString().substring(0, 16)
key = CryptoJS.enc.Base64.parse(key) key = CryptoJS.enc.Base64.parse(key)
let encryptedHex = CryptoJS.enc.Hex.parse(encryptedDataHexStr); let encryptedHex = CryptoJS.enc.Hex.parse(encryptedDataHexStr)
let encryptedBase64 = CryptoJS.enc.Base64.stringify(encryptedHex); let encryptedBase64 = CryptoJS.enc.Base64.stringify(encryptedHex)
var decryptedData = CryptoJS.AES.decrypt(encryptedBase64, key, { var decryptedData = CryptoJS.AES.decrypt(encryptedBase64, key, {
mode: CryptoJS.mode.ECB, mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7, padding: CryptoJS.pad.Pkcs7,
}); })
return decryptedData.toString(CryptoJS.enc.Utf8); return decryptedData.toString(CryptoJS.enc.Utf8)
} }
const base58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
const base58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
export const hexToBase58 = (hexString: string) => { export const hexToBase58 = (hexString: string) => {
const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)); const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))
let base58String = ''; let base58String = ''
let num = BigInt('0x' + hexString); let num = BigInt('0x' + hexString)
while (num > BigInt(0)) { while (num > BigInt(0)) {
const remainder = num % BigInt(58); const remainder = num % BigInt(58)
num = num / BigInt(58); num = num / BigInt(58)
base58String = base58Alphabet[Number(remainder)] + base58String; base58String = base58Alphabet[Number(remainder)] + base58String
} }
return base58String; return base58String
} }
export const base58ToHex = (base58String: string) => { export const base58ToHex = (base58String: string) => {
const base58Length = base58String.length; const base58Length = base58String.length
let num = BigInt(0); let num = BigInt(0)
let leadingZeros = 0; let leadingZeros = 0
for (let i = 0; i < base58Length; i++) { for (let i = 0; i < base58Length; i++) {
const charIndex = base58Alphabet.indexOf(base58String[i]); const charIndex = base58Alphabet.indexOf(base58String[i])
if (charIndex === -1) { if (charIndex === -1) {
throw new Error('Invalid Base58 string'); throw new Error('Invalid Base58 string')
} }
num = num * BigInt(58) + BigInt(charIndex); num = num * BigInt(58) + BigInt(charIndex)
} }
return num.toString(16); return num.toString(16)
} }
export const hexToBase32 = (hexString: string) => { export const hexToBase32 = (hexString: string) => {
const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)); const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))
const base32Alphabet = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; const base32Alphabet = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
let base32String = ''; let base32String = ''
let num = BigInt('0x' + hexString); let num = BigInt('0x' + hexString)
while (num > BigInt(0)) { while (num > BigInt(0)) {
const remainder = num % BigInt(32); const remainder = num % BigInt(32)
num = num / BigInt(32); num = num / BigInt(32)
base32String = base32Alphabet[Number(remainder)] + base32String; base32String = base32Alphabet[Number(remainder)] + base32String
} }
return base32String; return base32String
} }