change task id

This commit is contained in:
CounterFire2023 2024-01-04 19:08:33 +08:00
parent ff41a769b7
commit eb9ce07ff5
16 changed files with 178 additions and 55 deletions

View File

@ -101,12 +101,14 @@ SiweMessage的nonce说明(具体参考例子):
"tasks": [ // 该活动需要完成的任务 "tasks": [ // 该活动需要完成的任务
{ {
"id": "任务id", "id": "任务id",
"task": "任务类型",
"title": "任务名", "title": "任务名",
"desc": "任务描述", "desc": "任务描述",
"type": 1, //任务类型, 1: 一次性任务, 2: 日常任务 "type": 1, //任务类型, 1: 一次性任务, 2: 日常任务
"pretasks": ["task id 1"], //前置任务 "pretasks": ["task id 1"], //前置任务
"score": 0, // 完成任务可获得的积分 "score": 0, // 完成任务可获得的积分
"category": "", // 任务分类 "category": "", // 任务分类
"data": {}, // 其他一些任务相关配置参数, 比如icon, 或者其他未考虑的参数
"autoclaim": false // 任务完成后是否自动获取奖励 "autoclaim": false // 任务完成后是否自动获取奖励
} }
], ],

View File

@ -4,50 +4,108 @@
"name": "First Activity1", "name": "First Activity1",
"description": "This is the first test activity", "description": "This is the first test activity",
"tasks": [{ "tasks": [{
"id": "TwitterConnect", "id": "e2yhq2lj30vwcpedv7p",
"task": "TwitterConnect",
"title": "Connect Twitter", "title": "Connect Twitter",
"type": 1, "type": 1,
"desc": "", "desc": "",
"score": 100, "score": 100,
"category": "", "category": "",
"autoclaim": true, "autoclaim": true,
"data": {},
"params": {} "params": {}
}, { }, {
"id": "TwitterFollow", "id": "e2fclylj30vwcpe0szl",
"task": "TwitterFollow",
"title": "Follow Twitter", "title": "Follow Twitter",
"type": 1, "type": 1,
"desc": "", "desc": "Follow Counter Fires official X account",
"category": "", "category": "Social Tasks",
"score": 100, "score": 100,
"autoclaim": false, "autoclaim": false,
"pretasks": ["TwitterConnect"], "pretasks": ["TwitterConnect"],
"data": {},
"params": {"time": 6, "failRate": 60} "params": {"time": 6, "failRate": 60}
}, { }, {
"id": "OkxLogin", "id": "e2feyflj30vwcpe0sjy",
"title": "okx wallet login", "task": "TwitterFollow",
"title": "Follow Twitter",
"type": 1, "type": 1,
"desc": "", "desc": "",
"category": "", "category": "Social Tasks",
"score": 100, "score": 100,
"autoclaim": false, "autoclaim": false,
"pretasks": [], "pretasks": ["TwitterConnect"],
"params": {} "data": {},
"params": {"time": 6, "failRate": 60}
}, { }, {
"id": "TwitterRetweet", "id": "e2fuah0j30vwcpe0my7",
"title": "ReTwitt", "task": "TwitterRetweet",
"title": "ReTwitter",
"type": 2,
"desc": "",
"category": "Social Tasks",
"score": 100,
"autoclaim": false,
"pretasks": ["TwitterConnect"],
"data": {},
"params": {"time": 6, "failRate": 60}
}, {
"id": "e2far3lj30vwcpe0mh7",
"task": "DiscordConnect",
"title": "Connect Discord",
"type": 2, "type": 2,
"desc": "", "desc": "",
"category": "", "category": "",
"score": 100, "score": 100,
"autoclaim": false, "autoclaim": false,
"pretasks": ["TwitterConnect"], "pretasks": [],
"data": {},
"params": {}
}, {
"id": "e2far3lj30vwcpe0mf8",
"task": "DiscordJoin",
"title": "Join Discord",
"type": 2,
"desc": "",
"category": "",
"score": 100,
"autoclaim": false,
"pretasks": [],
"data": {},
"params": {"time": 6, "failRate": 60} "params": {"time": 6, "failRate": 60}
}, { }, {
"id": "UpdateScore", "id": "e2fak2lj30vwcpe0awc",
"task": "DiscordRole",
"title": "Discord Role",
"type": 2,
"desc": "",
"category": "Social Tasks",
"score": 100,
"autoclaim": false,
"pretasks": ["DiscordJoin"],
"data": {},
"params": {"time": 6, "failRate": 60}
}, {
"id": "e2f7fplj30vwcpe0l98",
"task": "OkxLogin",
"title": "okx wallet login",
"type": 1,
"desc": "",
"category": "Special Quests",
"score": 100,
"autoclaim": false,
"pretasks": [],
"data": {},
"params": {}
}, {
"id": "e2f7t4lj30vwcpe0ldr",
"task": "ShareCode",
"type": 1, "type": 1,
"show": false, "show": false,
"autoclaim": false, "autoclaim": false,
"pretasks": ["TwitterConnect", "TwitterFollow"], "pretasks": [],
"data": {},
"params": {"score": [100, 20]} "params": {"score": [100, 20]}
}], }],
"startTime": 1702628292366, "startTime": 1702628292366,

View File

@ -63,7 +63,7 @@ export default class ActivityController extends BaseController {
let user = await ActivityUser.findById(id) let user = await ActivityUser.findById(id)
results.push({ results.push({
rank: start + i / 2 + 1, rank: start + i / 2 + 1,
address: user.address, address: user?.address || 'unknow',
score score
}) })
} }

View File

@ -44,20 +44,20 @@ export default class TasksController extends BaseController {
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.id)) { if (!allTasks.has(task.task)) {
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, 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, 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);
} }
@ -132,7 +132,7 @@ export default class TasksController extends BaseController {
throw new ZError(11, 'task not begin'); throw new ZError(11, 'task not begin');
} }
if (currentTask.status === TaskStatusEnum.RUNNING) { if (currentTask.status === TaskStatusEnum.RUNNING) {
let Task = require('../tasks/' + taskId); 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});
} }
@ -155,7 +155,7 @@ export default class TasksController extends BaseController {
throw new ZError(13, 'task not end') throw new ZError(13, 'task not end')
} }
const Task = require('../tasks/' + 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

@ -15,6 +15,8 @@ export class TaskCfg {
@prop() @prop()
id: string id: string
@prop() @prop()
task: string
@prop()
title: string title: string
@prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE }) @prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE })
type: TaskTypeEnum type: TaskTypeEnum
@ -31,6 +33,8 @@ export class TaskCfg {
@prop({default: true}) @prop({default: true})
show: boolean show: boolean
@prop({ type: mongoose.Schema.Types.Mixed }) @prop({ type: mongoose.Schema.Types.Mixed })
data: any
@prop({ type: mongoose.Schema.Types.Mixed })
params: any params: any
} }
interface ActivityInfoClass extends Base, TimeStamps {} interface ActivityInfoClass extends Base, TimeStamps {}
@ -68,6 +72,7 @@ class ActivityInfoClass extends BaseModule {
if (task.show) { if (task.show) {
tasks.push({ tasks.push({
id: task.id, id: task.id,
task: task.task,
title: task.title, title: task.title,
desc: task.desc, desc: task.desc,
type: task.type, type: task.type,
@ -75,6 +80,7 @@ class ActivityInfoClass extends BaseModule {
score: task.score, score: task.score,
category: task.category, category: task.category,
autoclaim: task.autoclaim, autoclaim: task.autoclaim,
data: task.data,
}) })
} }
} }

View File

@ -19,6 +19,8 @@ export enum TaskStatusEnum {
export class TaskStatus { export class TaskStatus {
@prop() @prop()
id: string id: string
@prop()
task: string
// 0: not start, 1: running, 2: success // 0: not start, 1: running, 2: success
@prop({ enum: TaskStatusEnum, default: TaskStatusEnum.NOT_START }) @prop({ enum: TaskStatusEnum, default: TaskStatusEnum.NOT_START })
status: TaskStatusEnum status: TaskStatusEnum

View File

@ -0,0 +1,44 @@
import { checkDiscord } from "services/oauth.svr";
import { ITask } from "./base/ITask";
import { ZError } from "common/ZError";
import { TaskStatusEnum } from "models/ActivityUser";
import { TaskCfg } from "models/ActivityInfo";
export default class DiscordConnect extends ITask {
static desc = 'join discord'
static show: boolean = true
async execute(data: any) {
const { address } = this.params.user
const { task } = data
const res = await checkDiscord(address)
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
if (res.status !== 200) {
throw new ZError(11, 'discord check failed')
}
if (res.data.errcode) {
throw new ZError(res.data.errcode, res.data.errmsg)
}
if (res.data.data.userid && task.status === TaskStatusEnum.RUNNING) {
task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now()
task.data = res.data.data
task.discordId = res.data.data.userid
try {
await this.params.user.save()
} catch(err) {
throw new ZError(100, 'discord already binded')
}
if (cfg.autoclaim) {
try {
await this.claimReward(task);
} catch(err) {
console.log(err)
}
}
}
return true
}
}

View File

@ -1,4 +1,3 @@
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";
@ -9,32 +8,29 @@ export default class DiscordJoin extends ITask {
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
const { address } = this.params.user
const res = await checkDiscord(address)
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name)
if (res.status !== 200) {
throw new ZError(11, 'discord check failed')
}
if (res.data.errcode) {
throw new ZError(res.data.errcode, res.data.errmsg)
}
const { task } = data const { task } = data
if (res.data.data.userid && task.status === TaskStatusEnum.RUNNING) { let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
task.status = TaskStatusEnum.SUCCESS let time = cfg.params.time;
task.timeFinish = Date.now() if (Date.now() - task.timeStart < time * 1000) {
task.data = res.data.data throw new ZError(11, 'check discord join failed')
task.discordId = res.data.data.userid }
let num = Math.random() * 100
if (num < cfg.params.failRate) {
throw new ZError(12, 'check discord join failed')
}
task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now()
task.data = {}
try {
await this.params.user.save()
} catch(err) {
throw new ZError(100, 'already join discord')
}
if (cfg.autoclaim) {
try { try {
await this.params.user.save() await this.claimReward(task);
} catch(err) { } catch(err) {
throw new ZError(100, 'discord already binded') console.log(err)
}
if (cfg.autoclaim) {
try {
await this.claimReward(task);
} catch(err) {
console.log(err)
}
} }
} }
return true return true

View File

@ -8,7 +8,7 @@ export default class DiscordRole extends ITask {
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let cfg = this.params.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')

View File

@ -12,7 +12,7 @@ export default class OkxLogin extends ITask {
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
const { activity } = this.params.user const { activity } = this.params.user
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let wallet = 'okx'; let wallet = 'okx';
let record = LoginRecord.findOne({ user: this.params.user.id, activity, wallet}) let record = LoginRecord.findOne({ user: this.params.user.id, activity, wallet})
if (!record ) { if (!record ) {

View File

@ -29,8 +29,8 @@ export default class ShareCode extends ITask {
static show: boolean = false static show: boolean = false
async execute(data: any) { async execute(data: any) {
let task = this.params.user.taskProgress.find((t: TaskStatus) => t.id === this.constructor.name) let {task} = data
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let scores = cfg.params.score; let scores = cfg.params.score;
task.status = TaskStatusEnum.SUCCESS task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now() task.timeFinish = Date.now()
@ -39,7 +39,7 @@ export default class ShareCode extends ITask {
// 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 updateInviteScore(this.params.user, scores, 0, this.constructor.name) await updateInviteScore(this.params.user, scores, 0, task.task)
} catch(err) { } catch(err) {
console.log(err) console.log(err)
} }
@ -48,8 +48,8 @@ export default class ShareCode extends ITask {
} }
public async claimReward(task: TaskStatus) { public async claimReward(task: TaskStatus) {
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === task.id)
let scores = cfg.params.score; let scores = cfg.params.score;
await updateInviteScore(this.params.user, scores, 0, this.constructor.name) await updateInviteScore(this.params.user, scores, 0, task.task)
} }
} }

View File

@ -28,7 +28,7 @@ export default class TwitterConnect extends ITask {
} catch(err) { } catch(err) {
throw new ZError(100, 'twitter already binded') throw new ZError(100, 'twitter already binded')
} }
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let cfg = this.params.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);

View File

@ -8,7 +8,7 @@ export default class TwitterFollow extends ITask {
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let cfg = this.params.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')

View File

@ -7,7 +7,7 @@ export default class TwitterRetweet extends ITask {
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
const { task } = data const { task } = data
let cfg = this.params.activity.tasks.find(t => t.id === this.constructor.name) let cfg = this.params.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')

View File

@ -25,7 +25,7 @@ export abstract class ITask {
user: user.id, user: user.id,
score: cfg.score, score: cfg.score,
activity: user.activity, activity: user.activity,
scoreType: taskId, scoreType: cfg.task,
scoreParams: { scoreParams: {
date: dateTag, date: dateTag,
taskId: task.id taskId: task.id

View File

@ -136,4 +136,19 @@ export const base58ToHex = (base58String: 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) => {
const bytes = hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16));
const base32Alphabet = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
let base32String = '';
let num = BigInt('0x' + hexString);
while (num > BigInt(0)) {
const remainder = num % BigInt(32);
num = num / BigInt(32);
base32String = base32Alphabet[Number(remainder)] + base32String;
}
return base32String;
} }