增加nft burn, youtube follow, youtube post相关模块

This commit is contained in:
CounterFire2023 2024-01-09 16:02:58 +08:00
parent 970f004dad
commit bd2b7b577b
7 changed files with 321 additions and 31 deletions

View File

@ -86,6 +86,30 @@
"pretasks": ["e2far3lj30vwcpe0mf8"],
"cfg": {"icon": "discord"},
"params": {"time": 6, "failRate": 60}
}, {
"id": "e2feyflj30vwcpe0sjx",
"task": "YoutubeFollow",
"title": "Follow Youtube",
"type": 1,
"desc": "Follow Counter Fires 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",
"task": "OkxLogin",
@ -99,10 +123,10 @@
"cfg": {"account": "okx", "icon": "okx"},
"params": {}
}, {
"id": "e2f7fplj30vwcpe0l98",
"id": "e2f7fplj30vwcpe0l96",
"task": "DailyCheckIn",
"title": "daily checkin",
"type": 1,
"type": 2,
"desc": "",
"category": "Special Quests",
"score": 20,
@ -110,6 +134,18 @@
"pretasks": [],
"cfg": {"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",
"task": "ShareCode",
@ -120,6 +156,57 @@
"cfg": {},
"score": 100,
"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,
"endTime": 1705220292366

View 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'] })

View File

@ -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'
return fetch(url, {
method: 'POST',
@ -7,7 +7,31 @@ export const queryCheckInList = async (address: string, days: string | number |
body: JSON.stringify({
address,
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())
}

64
src/tasks/BurnNft.ts Normal file
View 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
}
}

View File

@ -1,8 +1,8 @@
import { ITask } from "./base/ITask";
import { ZError } from "common/ZError";
import { TaskStatus, TaskStatusEnum } from "models/ActivityUser";
import { TaskCfg } from "models/ActivityInfo";
import { queryCheckInList } from "services/chain.svr";
import { TaskCfg, TaskTypeEnum } from "models/ActivityInfo";
import { queryCheckInList, queryCheckInSeq } from "services/chain.svr";
import { updateRankScore } from "services/rank.svr";
// 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)
const days = cfg.params.days || 1
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) {
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.timeFinish = Date.now()
task.data = res.data
@ -48,33 +53,37 @@ export default class DailyCheckIn extends ITask {
}
public async claimReward(task: TaskStatus) {
// await super.claimReward(task);
// 增加连续签到奖励分
// 请求前7天的签到记录, 往前查找连续签到的记录,
const res = await queryCheckInList(this.params.user.address, 1, 0)
const [taskId, dateTag] = task.id.split(':');
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;
let count = list.length > 0 ? list[0].count : 0;
let seq = count % countCfg;
let score = cfg.params.score[seq] + cfg.score;
const user = this.params.user
if (user.boost > 1 && Date.now() < user.boostExpire) {
score = Math.floor(score * user.boost)
}
await updateRankScore({
user: this.params.user.id,
score: score,
activity: this.params.user.activity,
scoreType: cfg.task,
scoreParams: {
taskId: task.id,
date: dateTag,
bouns: score,
boost: user.boost,
if (cfg.type === TaskTypeEnum.DAILY) {
const res = await queryCheckInList(this.params.user.address, 1, 0)
const [taskId, dateTag] = task.id.split(':');
let list: { day: string, time: number, count: number }[] = res.data;
const countCfg = cfg.params.score.length;
let count = list.length > 0 ? list[0].count : 0;
let seq = count % countCfg;
let score = cfg.params.score[seq] || 0 + cfg.score;
const user = this.params.user
if (user.boost > 1 && Date.now() < user.boostExpire) {
score = Math.floor(score * user.boost)
}
})
await updateRankScore({
user: this.params.user.id,
score: score,
activity: this.params.user.activity,
scoreType: cfg.task,
scoreParams: {
taskId: task.id,
date: dateTag,
bouns: score,
boost: user.boost,
}
})
} else {
super.claimReward(task);
}
}
}

View 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
View 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
}
}