增加nft burn, youtube follow, youtube post相关模块
This commit is contained in:
parent
970f004dad
commit
bd2b7b577b
@ -86,6 +86,30 @@
|
|||||||
"pretasks": ["e2far3lj30vwcpe0mf8"],
|
"pretasks": ["e2far3lj30vwcpe0mf8"],
|
||||||
"cfg": {"icon": "discord"},
|
"cfg": {"icon": "discord"},
|
||||||
"params": {"time": 6, "failRate": 60}
|
"params": {"time": 6, "failRate": 60}
|
||||||
|
}, {
|
||||||
|
"id": "e2feyflj30vwcpe0sjx",
|
||||||
|
"task": "YoutubeFollow",
|
||||||
|
"title": "Follow Youtube",
|
||||||
|
"type": 1,
|
||||||
|
"desc": "Follow Counter Fire’s official YTB account",
|
||||||
|
"category": "Social Tasks",
|
||||||
|
"score": 100,
|
||||||
|
"autoclaim": false,
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {"icon": "youtube"},
|
||||||
|
"params": {"time": 6, "failRate": 60}
|
||||||
|
}, {
|
||||||
|
"id": "e2feyflj30vwcpe0sjz",
|
||||||
|
"task": "YoutubePost",
|
||||||
|
"title": "Post Youtube",
|
||||||
|
"type": 1,
|
||||||
|
"desc": "Post a video introducing @_CounterFire",
|
||||||
|
"category": "Social Tasks",
|
||||||
|
"score": 100,
|
||||||
|
"autoclaim": false,
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {"icon": "youtube"},
|
||||||
|
"params": {"time": 6, "failRate": 60}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2f7fplj30vwcpe0l98",
|
"id": "e2f7fplj30vwcpe0l98",
|
||||||
"task": "OkxLogin",
|
"task": "OkxLogin",
|
||||||
@ -99,10 +123,10 @@
|
|||||||
"cfg": {"account": "okx", "icon": "okx"},
|
"cfg": {"account": "okx", "icon": "okx"},
|
||||||
"params": {}
|
"params": {}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2f7fplj30vwcpe0l98",
|
"id": "e2f7fplj30vwcpe0l96",
|
||||||
"task": "DailyCheckIn",
|
"task": "DailyCheckIn",
|
||||||
"title": "daily checkin",
|
"title": "daily checkin",
|
||||||
"type": 1,
|
"type": 2,
|
||||||
"desc": "",
|
"desc": "",
|
||||||
"category": "Special Quests",
|
"category": "Special Quests",
|
||||||
"score": 20,
|
"score": 20,
|
||||||
@ -110,6 +134,18 @@
|
|||||||
"pretasks": [],
|
"pretasks": [],
|
||||||
"cfg": {"score": [0, 15, 20, 20, 40, 40, 60]},
|
"cfg": {"score": [0, 15, 20, 20, 40, 40, 60]},
|
||||||
"params": {"days": 1, "score": [0, 15, 20, 20, 40, 40, 60]}
|
"params": {"days": 1, "score": [0, 15, 20, 20, 40, 40, 60]}
|
||||||
|
}, {
|
||||||
|
"id": "e2f7fplj30vwcpe0l97",
|
||||||
|
"task": "DailyCheckIn",
|
||||||
|
"title": "daily checkin",
|
||||||
|
"type": 1,
|
||||||
|
"desc": "Check-in for 3 consecutive days.",
|
||||||
|
"category": "Referral to Earn",
|
||||||
|
"score": 100,
|
||||||
|
"autoclaim": false,
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {},
|
||||||
|
"params": {"days": 3}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2f7t4lj30vwcpe0ldr",
|
"id": "e2f7t4lj30vwcpe0ldr",
|
||||||
"task": "ShareCode",
|
"task": "ShareCode",
|
||||||
@ -120,6 +156,57 @@
|
|||||||
"cfg": {},
|
"cfg": {},
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"params": {"score": [100, 20]}
|
"params": {"score": [100, 20]}
|
||||||
|
}, {
|
||||||
|
"id": "e2f7t4lj31vwcpe0ldr",
|
||||||
|
"task": "BurnNft",
|
||||||
|
"type": 1,
|
||||||
|
"show": true,
|
||||||
|
"title": "Hero NFT",
|
||||||
|
"desc": "Click to burn and redeem Flame",
|
||||||
|
"category": "CF Pal",
|
||||||
|
"autoclaim": false,
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {"address": "0x59e751c2037B710090035B6ea928e0cce80aC03f"},
|
||||||
|
"score": 200,
|
||||||
|
"params": {"address": "0x59e751c2037B710090035B6ea928e0cce80aC03f"}
|
||||||
|
}, {
|
||||||
|
"id": "e2f7t4lj32vwcpe0ldr",
|
||||||
|
"task": "BurnNft",
|
||||||
|
"type": 1,
|
||||||
|
"show": true,
|
||||||
|
"title": "Candy Badge",
|
||||||
|
"desc": "Click to burn and redeem Flame",
|
||||||
|
"category": "CF Pal",
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {"address": "0x6a673D946a976776fd5F163d9d831b2fEB600015"},
|
||||||
|
"score": 100,
|
||||||
|
"params": {"address": "0x6a673D946a976776fd5F163d9d831b2fEB600015"}
|
||||||
|
}, {
|
||||||
|
"id": "e2f7t4lj33vwcpe0ldr",
|
||||||
|
"task": "BurnNft",
|
||||||
|
"type": 1,
|
||||||
|
"show": true,
|
||||||
|
"title": "Explorer Badge",
|
||||||
|
"desc": "Click to burn and redeem Flame",
|
||||||
|
"category": "CF Pal",
|
||||||
|
"autoclaim": false,
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {"address": "0x7b6399DFbed8Bc46F6A498C6B1040E80c2B5C4bc"},
|
||||||
|
"score": 100,
|
||||||
|
"params": {"address": "0x7b6399DFbed8Bc46F6A498C6B1040E80c2B5C4bc"}
|
||||||
|
}, {
|
||||||
|
"id": "e2f7t4lj33vwcpe0ldr",
|
||||||
|
"task": "BurnNft",
|
||||||
|
"type": 1,
|
||||||
|
"show": true,
|
||||||
|
"title": "Gacha Badge",
|
||||||
|
"desc": "Click to burn and redeem Flame",
|
||||||
|
"category": "CF Pal",
|
||||||
|
"autoclaim": false,
|
||||||
|
"pretasks": [],
|
||||||
|
"cfg": {"address": "0xe2E4D5a4045fBFcbCBECAf5b8A94303712d2FA97"},
|
||||||
|
"score": 200,
|
||||||
|
"params": {"address": "0xe2E4D5a4045fBFcbCBECAf5b8A94303712d2FA97"}
|
||||||
}],
|
}],
|
||||||
"startTime": 1702628292366,
|
"startTime": 1702628292366,
|
||||||
"endTime": 1705220292366
|
"endTime": 1705220292366
|
||||||
|
31
src/models/NFTBrunRecord.ts
Normal file
31
src/models/NFTBrunRecord.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Severity, getModelForClass, index, modelOptions, mongoose, prop } from '@typegoose/typegoose'
|
||||||
|
import { dbconn } from 'decorators/dbconn'
|
||||||
|
import { BaseModule } from './Base'
|
||||||
|
|
||||||
|
|
||||||
|
@dbconn()
|
||||||
|
@index({ user: 1, chain: 1, address: 1}, { unique: false })
|
||||||
|
@index({ user: 1, chain: 1, address: 1, tokenId: 1 }, { unique: true })
|
||||||
|
@modelOptions({ schemaOptions: { collection: 'nft_burn_record', timestamps: true }, options: { allowMixed: Severity.ALLOW } })
|
||||||
|
class NftBurnRecordClass extends BaseModule {
|
||||||
|
@prop({ required: true})
|
||||||
|
public user: string
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public chain: number
|
||||||
|
|
||||||
|
@prop({ required: true})
|
||||||
|
public address: string
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public tokenId: number
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public activity: string
|
||||||
|
@prop()
|
||||||
|
public task: string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NftBurnRecord = getModelForClass(NftBurnRecordClass, { existingConnection: NftBurnRecordClass['db'] })
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
export const queryCheckInList = async (address: string, days: string | number | string[], max: 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',
|
||||||
@ -7,7 +7,31 @@ export const queryCheckInList = async (address: string, days: string | number |
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
address,
|
address,
|
||||||
days,
|
days,
|
||||||
max
|
limit
|
||||||
|
})
|
||||||
|
}).then((res) => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryCheckInSeq = async (address: string) =>{
|
||||||
|
const url = process.env.CHAIN_SVR + '/task/check_in/max_seq'
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: JSON.stringify({
|
||||||
|
address,
|
||||||
|
})
|
||||||
|
}).then((res) => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryBurnNftList = async (address: string, user: string, chain: number) => {
|
||||||
|
const url = process.env.CHAIN_SVR + '/task/nft/checkburn'
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
body: JSON.stringify({
|
||||||
|
address,
|
||||||
|
user,
|
||||||
|
chain
|
||||||
})
|
})
|
||||||
}).then((res) => res.json())
|
}).then((res) => res.json())
|
||||||
}
|
}
|
64
src/tasks/BurnNft.ts
Normal file
64
src/tasks/BurnNft.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { ITask } from "./base/ITask";
|
||||||
|
import { TaskStatusEnum } from "models/ActivityUser";
|
||||||
|
import { ZError } from "common/ZError";
|
||||||
|
import { TaskCfg } from "models/ActivityInfo";
|
||||||
|
import { queryBurnNftList } from "services/chain.svr";
|
||||||
|
import { NftBurnRecord } from "models/NFTBrunRecord";
|
||||||
|
|
||||||
|
export default class BurnNft extends ITask {
|
||||||
|
static desc = 'Butn NFT'
|
||||||
|
static show: boolean = true
|
||||||
|
async execute(data: any) {
|
||||||
|
const { task } = data
|
||||||
|
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
|
||||||
|
const address = cfg.params.address.toLowerCase()
|
||||||
|
const chain = parseInt(process.env.CHAIN)
|
||||||
|
const res = await queryBurnNftList(address, this.params.user.address, chain)
|
||||||
|
if (res.errcode) {
|
||||||
|
throw new ZError(res.errcode, res.errmsg)
|
||||||
|
}
|
||||||
|
const nftList = res.data
|
||||||
|
const localNft = await NftBurnRecord.find({
|
||||||
|
user: this.params.user.id,
|
||||||
|
chain,
|
||||||
|
address
|
||||||
|
})
|
||||||
|
const localNftSet = new Set();
|
||||||
|
for (let nft of localNft) {
|
||||||
|
localNftSet.add(nft.tokenId)
|
||||||
|
}
|
||||||
|
let finishNft
|
||||||
|
for (let nft of nftList) {
|
||||||
|
if (!localNftSet.has(nft.tokenId)) {
|
||||||
|
finishNft = nft.tokenId
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const record = new NftBurnRecord({
|
||||||
|
user: this.params.user.id,
|
||||||
|
chain,
|
||||||
|
address,
|
||||||
|
tokenId: finishNft,
|
||||||
|
activity: this.params.activity.id,
|
||||||
|
task: task.id
|
||||||
|
})
|
||||||
|
await record.save()
|
||||||
|
task.status = TaskStatusEnum.SUCCESS
|
||||||
|
task.timeFinish = Date.now()
|
||||||
|
task.data = {tokenId: finishNft}
|
||||||
|
try {
|
||||||
|
await this.params.user.save()
|
||||||
|
} catch(err) {
|
||||||
|
throw new ZError(100, 'save failed')
|
||||||
|
}
|
||||||
|
if (cfg.autoclaim) {
|
||||||
|
try {
|
||||||
|
await this.claimReward(task);
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
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 } from "models/ActivityInfo";
|
import { TaskCfg, TaskTypeEnum } from "models/ActivityInfo";
|
||||||
import { queryCheckInList } from "services/chain.svr";
|
import { queryCheckInList, queryCheckInSeq } from "services/chain.svr";
|
||||||
import { updateRankScore } from "services/rank.svr";
|
import { updateRankScore } from "services/rank.svr";
|
||||||
|
|
||||||
// TODO:: test
|
// TODO:: test
|
||||||
@ -22,12 +22,17 @@ export default class DailyCheckIn extends ITask {
|
|||||||
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
|
let cfg = this.params.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 = await queryCheckInList(address, days - 1, limit)
|
const res = cfg.type === TaskTypeEnum.DAILY ?
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
let success = false
|
||||||
|
|
||||||
if (task.status === TaskStatusEnum.RUNNING && res.data.length >= days) {
|
|
||||||
|
if ((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
|
||||||
@ -48,17 +53,18 @@ export default class DailyCheckIn extends ITask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async claimReward(task: TaskStatus) {
|
public async claimReward(task: TaskStatus) {
|
||||||
// await super.claimReward(task);
|
|
||||||
// 增加连续签到奖励分
|
// 增加连续签到奖励分
|
||||||
// 请求前7天的签到记录, 往前查找连续签到的记录,
|
// 请求前7天的签到记录, 往前查找连续签到的记录,
|
||||||
|
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
|
||||||
|
if (cfg.type === TaskTypeEnum.DAILY) {
|
||||||
const res = await queryCheckInList(this.params.user.address, 1, 0)
|
const res = await queryCheckInList(this.params.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;
|
||||||
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
|
|
||||||
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] + cfg.score;
|
let score = cfg.params.score[seq] || 0 + cfg.score;
|
||||||
const user = this.params.user
|
const user = this.params.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)
|
||||||
@ -75,6 +81,9 @@ export default class DailyCheckIn extends ITask {
|
|||||||
boost: user.boost,
|
boost: user.boost,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
super.claimReward(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
38
src/tasks/YoutubeFollow.ts
Normal file
38
src/tasks/YoutubeFollow.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { ITask } from "./base/ITask";
|
||||||
|
import { TaskStatusEnum } from "models/ActivityUser";
|
||||||
|
import { ZError } from "common/ZError";
|
||||||
|
import { TaskCfg } from "models/ActivityInfo";
|
||||||
|
|
||||||
|
export default class YoutubeFollow extends ITask {
|
||||||
|
static desc = 'youtube follow'
|
||||||
|
static show: boolean = true
|
||||||
|
async execute(data: any) {
|
||||||
|
const { task } = data
|
||||||
|
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
|
||||||
|
let time = cfg.params.time;
|
||||||
|
if (Date.now() - task.timeStart < time * 1000) {
|
||||||
|
throw new ZError(11, 'follow failed')
|
||||||
|
}
|
||||||
|
let num = Math.random() * 100
|
||||||
|
if (num < cfg.params.failRate) {
|
||||||
|
throw new ZError(12, 'follow failed')
|
||||||
|
}
|
||||||
|
task.status = TaskStatusEnum.SUCCESS
|
||||||
|
task.timeFinish = Date.now()
|
||||||
|
task.data = {}
|
||||||
|
try {
|
||||||
|
await this.params.user.save()
|
||||||
|
} catch(err) {
|
||||||
|
throw new ZError(100, 'save failed')
|
||||||
|
}
|
||||||
|
if (cfg.autoclaim) {
|
||||||
|
try {
|
||||||
|
await this.claimReward(task);
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/tasks/YoutubePost.ts
Normal file
37
src/tasks/YoutubePost.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { ZError } from "common/ZError";
|
||||||
|
import { ITask } from "./base/ITask";
|
||||||
|
import { TaskStatusEnum } from "models/ActivityUser";
|
||||||
|
|
||||||
|
export default class YoutubePost extends ITask {
|
||||||
|
static desc = 'youtube post'
|
||||||
|
static show: boolean = true
|
||||||
|
async execute(data: any) {
|
||||||
|
const { task } = data
|
||||||
|
let cfg = this.params.activity.tasks.find(t => t.id === task.id)
|
||||||
|
let time = cfg.params.time;
|
||||||
|
if (Date.now() - task.timeStart < time * 1000) {
|
||||||
|
throw new ZError(11, 'post failed')
|
||||||
|
}
|
||||||
|
let num = Math.random() * 100
|
||||||
|
if (num < cfg.params.failRate) {
|
||||||
|
throw new ZError(12, 'post failed')
|
||||||
|
}
|
||||||
|
task.status = TaskStatusEnum.SUCCESS
|
||||||
|
task.timeFinish = Date.now()
|
||||||
|
task.data = {}
|
||||||
|
try {
|
||||||
|
await this.params.user.save()
|
||||||
|
} catch(err) {
|
||||||
|
throw new ZError(100, 'save failed')
|
||||||
|
}
|
||||||
|
if (cfg.autoclaim) {
|
||||||
|
try {
|
||||||
|
await this.claimReward(task);
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user