From e9b0f802c1edffcb045a26d5b1ad6e402eb6901a Mon Sep 17 00:00:00 2001 From: CounterFire2023 <136581895+CounterFire2023@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:10:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9task=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api.md | 40 ++++- initdatas/activity_info.json | 23 ++- src/controllers/activity.controller.ts | 10 +- src/controllers/tasks.controller.ts | 186 ++++++++++++--------- src/models/ActivityInfo.ts | 36 ++++ src/models/ActivityUser.ts | 7 +- src/models/ScoreRecord.ts | 7 +- src/services/rank.svr.ts | 43 +++++ src/tasks/DiscordJoin.ts | 19 ++- src/tasks/DiscordRole.ts | 18 +- src/tasks/{UpdateScore.ts => ShareCode.ts} | 36 ++-- src/tasks/TwitterConnect.ts | 14 +- src/tasks/TwitterFollow.ts | 18 +- src/tasks/TwitterRetweet.ts | 15 +- src/tasks/base/ITask.ts | 23 +++ src/utils/date.util.ts | 7 + 16 files changed, 368 insertions(+), 134 deletions(-) create mode 100644 src/services/rank.svr.ts rename src/tasks/{UpdateScore.ts => ShareCode.ts} (59%) create mode 100644 src/utils/date.util.ts diff --git a/docs/api.md b/docs/api.md index 31c6ca1..f2f41b0 100644 --- a/docs/api.md +++ b/docs/api.md @@ -93,7 +93,12 @@ SiweMessage说明: https://docs.login.xyz/sign-in-with-ethereum/quickstart-guide { "id": "任务id", "title": "任务名", - "desc": "任务描述" + "desc": "任务描述", + "type": 1, //任务类型, 1: 一次性任务, 2: 日常任务 + "pretasks": ["task id 1"], //前置任务 + "score": 0, // 完成任务可获得的积分 + "category": "", // 任务分类 + "autoclaim": false // 任务完成后是否自动获取奖励 } ], "startTime": 1702628292366, // 活动开始时间 @@ -188,7 +193,36 @@ body: } ``` -### 6.\* 提交邀请码 +###7.\* 获取任务奖励 + +#### Request + +- URL:`/api/tasks/claim` +- 方法:`GET` +- 头部: + - Authorization: Bearer JWT_token + +body: + +```js +{ + "task": "TwitterFollow" // 任务id +} + +``` + +#### Response + +```json +{ + "status": 1, // 任务状态, 0: 未开始, 1: 进行中, 2: 成功, 9: 失败 + "id": "TwitterFollow", // 任务id + "timeStart": 1703150294051, // 任务开始时间 + "timeFinish": 1703151338598 + } +``` + +### 8.\* 提交邀请码 #### Request @@ -213,7 +247,7 @@ body: {} ``` -### 7. 积分排行榜 +### 9. 积分排行榜 #### Request diff --git a/initdatas/activity_info.json b/initdatas/activity_info.json index b8f3d5b..855438d 100644 --- a/initdatas/activity_info.json +++ b/initdatas/activity_info.json @@ -6,16 +6,35 @@ "tasks": [{ "id": "TwitterConnect", "title": "Connect Twitter", + "type": 1, "desc": "", - "params": {} + "category": "", + "autoclaim": true, + "params": {"score": 100} }, { "id": "TwitterFollow", "title": "Follow Twitter", + "type": 1, "desc": "", - "params": {"time": 6, "failRate": 60} + "category": "", + "autoclaim": false, + "pretasks": ["TwitterConnect"], + "params": {"score": 100, "time": 6, "failRate": 60} + }, { + "id": "TwitterRetweet", + "title": "ReTwitt", + "type": 2, + "desc": "", + "category": "", + "autoclaim": false, + "pretasks": ["TwitterConnect"], + "params": {"score": 100, "time": 6, "failRate": 60} }, { "id": "UpdateScore", + "type": 1, "show": false, + "autoclaim": false, + "pretasks": ["TwitterConnect", "TwitterFollow"], "params": {"score": [100, 20]} }], "startTime": 1702628292366, diff --git a/src/controllers/activity.controller.ts b/src/controllers/activity.controller.ts index d6cc562..47b4fe1 100644 --- a/src/controllers/activity.controller.ts +++ b/src/controllers/activity.controller.ts @@ -19,15 +19,7 @@ export default class ActivityController extends BaseController { if (!activity) { throw new ZError(12, 'activity not found') } - let tasks = [] - for (let task of activity.tasks) { - if (task.show) { - tasks.push({id: task.id, title: task.title, desc: task.desc}) - } - } - let result = activity.toJson() - result.tasks = tasks - return result; + return activity.toJson() } /** diff --git a/src/controllers/tasks.controller.ts b/src/controllers/tasks.controller.ts index c5089b9..5846420 100644 --- a/src/controllers/tasks.controller.ts +++ b/src/controllers/tasks.controller.ts @@ -1,10 +1,10 @@ import { ZError } from "common/ZError"; import BaseController, { ROLE_ANON } from "common/base.controller"; -import { role, router } from "decorators/router"; -import { all } from "deepmerge"; -import { ActivityInfo, TaskCfg } from "models/ActivityInfo"; +import { router } from "decorators/router"; +import { ActivityInfo, TaskCfg, TaskTypeEnum } from "models/ActivityInfo"; import { ActivityUser, TaskStatus, TaskStatusEnum } from "models/ActivityUser"; import { join } from 'path' +import { formatDate } from "utils/date.util"; const fs = require('fs') const prod = process.env.NODE_ENV === 'production' @@ -25,82 +25,46 @@ const initTasks = () => { } const allTasks = initTasks(); -const findNextTask = (user: typeof ActivityUser, task: string) => { - let currentTask = null; - for (let taskData of user.taskProgress) { - if (taskData.id === task) { - currentTask = taskData; - continue; - } - if (currentTask && currentTask.status === TaskStatusEnum.SUCCESS) { - return taskData; - } - } - return null; -} -const parseCurrentTask = (user: typeof ActivityUser, task: string) => { - if (!allTasks.has(task)) { - throw new ZError(11, 'invalid task') - } - let preEnd = true; - let currentTask = null; - for (let taskData of user.taskProgress) { - if (taskData.id === task) { - currentTask = taskData; - break; - } - if (taskData.status !== TaskStatusEnum.SUCCESS && !currentTask) { - preEnd = false; - } - } - if (!preEnd) { - throw new ZError(12, 'task previous not end') - } - if (!currentTask) { - throw new ZError(13, 'task not found') - } - return {preEnd, currentTask} -} - -const parseNextTask = async ( - user: typeof ActivityUser, - activity: typeof ActivityInfo, - task: string - ) => { - let nextTask = findNextTask(user, task); - if (!nextTask) { - return true - } - let Task = require('../tasks/' + nextTask.id); - if (!Task.default.auto) { - return true - } - let taskInstance = new Task.default({user, activity}); - let result = await taskInstance.execute({}); - await parseNextTask(user, activity, nextTask.id); - return result -} export default class TasksController extends BaseController { @router('post /api/tasks/progress') async taskProgress(req) { let user = req.user; let activity = req.activity; - if (!user.taskProgress || user.taskProgress.length === 0) { - for (let task of activity.tasks) { - if (allTasks.has(task.id)) { + const dateTag = formatDate(new Date()); + let taskAddedSet = new Set(); + for (let task of user.taskProgress) { + if (task.dateTag) { + taskAddedSet.add(task.id + ':' + task.dateTag) + } else { + taskAddedSet.add(task.id) + } + } + let modified = false; + let visibleTasks = new Set(); + for (let task of activity.tasks) { + if (!allTasks.has(task.id)) { + continue; + } + if (task.type === TaskTypeEnum.DAILY ) { + let id = `${task.id}:${dateTag}` + if (!taskAddedSet.has(id)) { + modified = true; + user.taskProgress.push({id, dateTag: dateTag, status: TaskStatusEnum.NOT_START}) + } + if (task.show) visibleTasks.add(id); + } else if (task.type === TaskTypeEnum.ONCE ) { + if (!taskAddedSet.has(task.id)) { + modified = true; user.taskProgress.push({id: task.id, status: TaskStatusEnum.NOT_START}) } + if (task.show) visibleTasks.add(task.id); } + } + if (modified) { await user.save(); } - let visibleTasks = new Set(); - activity.tasks.forEach((t:TaskCfg) => { - if (t.show) { - visibleTasks.add(t.id) - } - }) return user.taskProgress.filter((t: TaskStatus) => visibleTasks.has(t.id)); } @@ -108,35 +72,93 @@ export default class TasksController extends BaseController { @router('post /api/tasks/begin_task') async beginTask(req) { let user = req.user; + let activity = req.activity; let { task } = req.params; - let { currentTask } = parseCurrentTask(user, task); - currentTask.timeStart = Date.now(); - currentTask.status = 1; + const [taskId, dateTag] = task.split(':'); + const currentDateTag = formatDate(new Date()); + if (currentDateTag !== dateTag) { + throw new ZError(11, 'task date not match') + } + let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId); + if (!cfg) { + throw new ZError(12, 'task not found') + } + if (dateTag && cfg.type !== TaskTypeEnum.DAILY) { + throw new ZError(13, 'task is not daily task') + } + if (cfg.pretasks && cfg.pretasks.length > 0) { + for (let preTask of cfg.pretasks) { + let preTaskData = user.taskProgress.find((t: TaskStatus) => { + if (preTask.type === TaskTypeEnum.DAILY) { + return t.id === `${preTask}:${formatDate(new Date())}` + } + return t.id === preTask + }); + if (!preTaskData || (preTaskData.status === TaskStatusEnum.NOT_START || preTaskData.status === TaskStatusEnum.RUNNING)) { + throw new ZError(14, 'task previous not end') + } + } + } + let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); + if (currentTask.status === TaskStatusEnum.SUCCESS || currentTask.status === TaskStatusEnum.CLAIMED) { + throw new ZError(15, 'task already end') + } + if (currentTask.status === TaskStatusEnum.NOT_START) { + currentTask.timeStart = Date.now(); + currentTask.status = TaskStatusEnum.RUNNING; + } await user.save(); return currentTask } @router('post /api/tasks/check_task') async checkTask(req) { - let user = req.user; - let activity = req.activity; - let { task } = req.params; - let { currentTask } = parseCurrentTask(user, task); - if (currentTask.status === TaskStatusEnum.SUCCESS) { + const user = req.user; + const activity = req.activity; + const { task } = req.params; + const [taskId, dateTag] = task.split(':'); + const currentDateTag = formatDate(new Date()); + if (currentDateTag !== dateTag) { + throw new ZError(11, 'task date not match') + } + let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); + if (!currentTask) { + throw new ZError(11, 'task not found') + } + if (currentTask.status === TaskStatusEnum.SUCCESS || currentTask.status === TaskStatusEnum.CLAIMED) { return currentTask; } - if (currentTask.status !== TaskStatusEnum.RUNNING) { + if (currentTask.status === TaskStatusEnum.NOT_START) { throw new ZError(11, 'task not begin'); } - if (currentTask.status !== TaskStatusEnum.SUCCESS) { - let Task = require('../tasks/' + task); + if (currentTask.status === TaskStatusEnum.RUNNING) { + let Task = require('../tasks/' + taskId); let taskInstance = new Task.default({user, activity}); - let result = await taskInstance.execute({}); - if (result) { - await parseNextTask(user, activity, task); - } + await taskInstance.execute({task: currentTask}); } return currentTask } + @router('post /api/tasks/claim') + async claimTask(req) { + const user = req.user; + const activity = req.activity; + const { task } = req.params; + let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); + if (!currentTask) { + throw new ZError(11, 'task not found') + } + if (currentTask.status === TaskStatusEnum.CLAIMED) { + throw new ZError(12, 'task already claimed') + } + if (currentTask.status !== TaskStatusEnum.SUCCESS) { + throw new ZError(13, 'task not end') + } + + const Task = require('../tasks/' + task); + const taskInstance = new Task.default({user, activity}); + await taskInstance.claimReward(currentTask); + return currentTask + } + } \ No newline at end of file diff --git a/src/models/ActivityInfo.ts b/src/models/ActivityInfo.ts index c55cf7d..78a24b1 100644 --- a/src/models/ActivityInfo.ts +++ b/src/models/ActivityInfo.ts @@ -4,15 +4,30 @@ import { dbconn } from 'decorators/dbconn' import findOrCreate from 'mongoose-findorcreate' import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses' import { BaseModule } from './Base' +import exp from 'constants' +export enum TaskTypeEnum { + ONCE = 1, + DAILY = 2, +} @modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }, }) export class TaskCfg { @prop() id: string @prop() title: string + @prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE }) + type: TaskTypeEnum @prop() desc: string + @prop({ type: mongoose.Schema.Types.Mixed }) + category: any + @prop({default: false}) + autoclaim: boolean + @prop({ type: () => [String], default: [] }) + pretasks: string[] + @prop() + score: number @prop({default: true}) show: boolean @prop({ type: mongoose.Schema.Types.Mixed }) @@ -45,6 +60,27 @@ class ActivityInfoClass extends BaseModule { @prop() public comment?: string + + public toJson() { + let result = super.toJson() + let tasks = [] + for (let task of this.tasks) { + if (task.show) { + tasks.push({ + id: task.id, + title: task.title, + desc: task.desc, + type: task.type, + pretasks: task.pretasks, + score: task.score, + category: task.category, + autoclaim: task.autoclaim, + }) + } + } + result.tasks = tasks + return result + } } diff --git a/src/models/ActivityUser.ts b/src/models/ActivityUser.ts index 4910488..0cf55e8 100644 --- a/src/models/ActivityUser.ts +++ b/src/models/ActivityUser.ts @@ -12,6 +12,7 @@ export enum TaskStatusEnum { NOT_START = 0, RUNNING = 1, SUCCESS = 2, + CLAIMED = 3, } @modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }}) @@ -22,9 +23,13 @@ export class TaskStatus { @prop({ enum: TaskStatusEnum, default: TaskStatusEnum.NOT_START }) status: TaskStatusEnum @prop() + dateTag: string + @prop() timeStart: number @prop() timeFinish: number + @prop() + timeClaim: number @prop({ type: mongoose.Schema.Types.Mixed }) data: any } @@ -73,7 +78,7 @@ class ActivityUserClass extends BaseModule { @prop() public twitterId?: string @prop() - public discordId: string + public discordId?: string @prop() public lastLogin?: Date diff --git a/src/models/ScoreRecord.ts b/src/models/ScoreRecord.ts index 03bf720..893fb59 100644 --- a/src/models/ScoreRecord.ts +++ b/src/models/ScoreRecord.ts @@ -2,9 +2,6 @@ import { Severity, getModelForClass, index, modelOptions, mongoose, prop } from import { dbconn } from 'decorators/dbconn' import { BaseModule } from './Base' -export enum ScoreTypeEnum { - INVITE = 1, -} @dbconn() @index({ user: 1 }, { unique: false }) @@ -20,8 +17,8 @@ class ScoreRecordClass extends BaseModule { @prop() public score: number - @prop({ enum: ScoreTypeEnum }) - public type: ScoreTypeEnum + @prop() + public type: string @prop({ type: mongoose.Schema.Types.Mixed }) public data: any diff --git a/src/services/rank.svr.ts b/src/services/rank.svr.ts new file mode 100644 index 0000000..7eaa0c4 --- /dev/null +++ b/src/services/rank.svr.ts @@ -0,0 +1,43 @@ +import { ScoreRecord } from "models/ScoreRecord"; +import { RedisClient } from "redis/RedisClient"; + +/** + * 更新排行榜 + * @param param0 + * user: 用户id + * score: 分数 + * activity: 活动id + * scoreType: 分数类型 + * scoreParams: 额外的参数 + */ +export const updateRankScore = async ({ + user, + score, + activity, + scoreType, + scoreParams +}: { + user: string, + score: number, + activity: string, + scoreType: string, + scoreParams: any +}) => { + let record = new ScoreRecord({ + user: user, + activity: activity, + score, + type: scoreType, + data: scoreParams + }) + await record.save(); + const key = `${activity}:score` + let scoreSaved = await new RedisClient().zscore(key, user) + ''; + if (scoreSaved) { + scoreSaved = scoreSaved.substring(0, scoreSaved.indexOf('.')) + } + let scoreOld = parseInt(scoreSaved || '0'); + score = score + scoreOld; + const scoreToSave = score + 1 - (Date.now() / 1000 / 10000000000) + await new RedisClient().zadd(key, scoreToSave, user); +} \ No newline at end of file diff --git a/src/tasks/DiscordJoin.ts b/src/tasks/DiscordJoin.ts index c4b97a1..53866df 100644 --- a/src/tasks/DiscordJoin.ts +++ b/src/tasks/DiscordJoin.ts @@ -2,23 +2,24 @@ 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 DiscordJoin extends ITask { static desc = 'join discord' static show: boolean = true async execute(data: any) { - let { address } = this.params.user - let res = await checkDiscord(address) - console.log(res); + 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) } - let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name) - if (res.data.data.userid && task.status !== TaskStatusEnum.SUCCESS) { + const { task } = data + if (res.data.data.userid && task.status === TaskStatusEnum.RUNNING) { task.status = TaskStatusEnum.SUCCESS task.timeFinish = Date.now() task.data = res.data.data @@ -28,7 +29,13 @@ export default class DiscordJoin extends ITask { } catch(err) { throw new ZError(100, 'discord already binded') } - + if (cfg.autoclaim) { + try { + await this.claimReward(task); + } catch(err) { + console.log(err) + } + } } return true } diff --git a/src/tasks/DiscordRole.ts b/src/tasks/DiscordRole.ts index 17b6280..ce9d365 100644 --- a/src/tasks/DiscordRole.ts +++ b/src/tasks/DiscordRole.ts @@ -1,13 +1,14 @@ import { ZError } from "common/ZError"; import { ITask } from "./base/ITask"; import { TaskStatusEnum } from "models/ActivityUser"; +import { TaskCfg } from "models/ActivityInfo"; export default class DiscordRole extends ITask { static desc = 'acquire discord role' static show: boolean = true async execute(data: any) { - let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name) - let cfg = this.params.activity.tasks.find(t => t.id === this.constructor.name) + const { task } = data + let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let time = cfg.params.time; if (Date.now() - task.timeStart < time * 1000) { throw new ZError(11, 'check discord role failed') @@ -19,7 +20,18 @@ export default class DiscordRole extends ITask { task.status = TaskStatusEnum.SUCCESS task.timeFinish = Date.now() task.data = {} - await this.params.user.save() + try { + await this.params.user.save() + } catch(err) { + throw new ZError(100, 'already acquired discord role') + } + if (cfg.autoclaim) { + try { + await this.claimReward(task); + } catch(err) { + console.log(err) + } + } return true } diff --git a/src/tasks/UpdateScore.ts b/src/tasks/ShareCode.ts similarity index 59% rename from src/tasks/UpdateScore.ts rename to src/tasks/ShareCode.ts index bea86a8..9a29997 100644 --- a/src/tasks/UpdateScore.ts +++ b/src/tasks/ShareCode.ts @@ -1,10 +1,9 @@ import { ActivityUser, TaskStatus, TaskStatusEnum } from "models/ActivityUser"; import { ITask } from "./base/ITask"; import { TaskCfg } from "models/ActivityInfo"; -import { ScoreRecord, ScoreTypeEnum } from "models/ScoreRecord"; -import { RedisClient } from "redis/RedisClient"; +import { updateRankScore } from "services/rank.svr"; -const updateInviteScore = async (user: typeof ActivityUser, scores: number[], level: number) => { +const updateInviteScore = async (user: typeof ActivityUser, scores: number[], level: number, reason: string) => { if (!user.inviteUser || scores.length <= level) { return; } @@ -12,27 +11,23 @@ const updateInviteScore = async (user: typeof ActivityUser, scores: number[], le if (!userSup) { return; } - let record = new ScoreRecord({ + await updateRankScore({ user: userSup.id, - activity: user.activity, score: scores[level], - type: ScoreTypeEnum.INVITE, - data: { + activity: user.activity, + scoreType: reason, + scoreParams: { user: user.id, level } }) - await record.save(); - const key = `${user.activity}:score` - const score = scores[level] + 1 - (Date.now() / 1000 / 10000000000) - await new RedisClient().zincrby(key, score, userSup.id); - await updateInviteScore(userSup, scores, level + 1) + await updateInviteScore(userSup, scores, level + 1, reason) } -export default class UpdateScore extends ITask { +export default class ShareCode extends ITask { static desc = 'update invite score' static show: boolean = false - static auto: boolean = true + async execute(data: any) { let task = this.params.user.taskProgress.find((t: TaskStatus) => t.id === this.constructor.name) let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) @@ -42,8 +37,19 @@ export default class UpdateScore extends ITask { task.data = {} await this.params.user.save(); // According to configuration, add score to user who invite current user - await updateInviteScore(this.params.user, scores, 0) + if (cfg.autoclaim) { + try { + await updateInviteScore(this.params.user, scores, 0, this.constructor.name) + } catch(err) { + console.log(err) + } + } return true } + public async claimReward(task: TaskStatus) { + let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) + let scores = cfg.params.score; + await updateInviteScore(this.params.user, scores, 0, this.constructor.name) + } } \ No newline at end of file diff --git a/src/tasks/TwitterConnect.ts b/src/tasks/TwitterConnect.ts index c566cf8..37fb530 100644 --- a/src/tasks/TwitterConnect.ts +++ b/src/tasks/TwitterConnect.ts @@ -2,6 +2,7 @@ import { checkTwitter } from "services/oauth.svr"; import { ITask } from "./base/ITask"; import { ActivityUser, TaskStatusEnum } from "models/ActivityUser"; import { ZError } from "common/ZError"; +import { TaskCfg } from "models/ActivityInfo"; export default class TwitterConnect extends ITask { static desc = 'twitter connect' @@ -10,15 +11,14 @@ export default class TwitterConnect extends ITask { async execute(data: any) { let { address } = this.params.user let res = await checkTwitter(address) - console.log(res); if (res.status !== 200) { throw new ZError(11, 'twitter check failed') } if (res.data.errcode) { throw new ZError(res.data.errcode, res.data.errmsg) } - let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name) - if (res.data.data.userid && task.status !== TaskStatusEnum.SUCCESS) { + const { task } = data + if (res.data.data.userid && task.status === TaskStatusEnum.RUNNING) { task.status = TaskStatusEnum.SUCCESS task.timeFinish = Date.now() task.data = res.data.data @@ -28,6 +28,14 @@ export default class TwitterConnect extends ITask { } catch(err) { throw new ZError(100, 'twitter already binded') } + let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) + if (cfg.autoclaim) { + try { + await this.claimReward(task); + } catch(err) { + console.log(err) + } + } } return true diff --git a/src/tasks/TwitterFollow.ts b/src/tasks/TwitterFollow.ts index d4b04e4..490c22c 100644 --- a/src/tasks/TwitterFollow.ts +++ b/src/tasks/TwitterFollow.ts @@ -1,13 +1,14 @@ import { ITask } from "./base/ITask"; import { TaskStatusEnum } from "models/ActivityUser"; import { ZError } from "common/ZError"; +import { TaskCfg } from "models/ActivityInfo"; export default class TwitterFollow extends ITask { static desc = 'twitter follow' static show: boolean = true async execute(data: any) { - let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name) - let cfg = this.params.activity.tasks.find(t => t.id === this.constructor.name) + const { task } = data + let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === this.constructor.name) let time = cfg.params.time; if (Date.now() - task.timeStart < time * 1000) { throw new ZError(11, 'follow failed') @@ -19,7 +20,18 @@ export default class TwitterFollow extends ITask { task.status = TaskStatusEnum.SUCCESS task.timeFinish = Date.now() task.data = {} - await this.params.user.save() + 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 } diff --git a/src/tasks/TwitterRetweet.ts b/src/tasks/TwitterRetweet.ts index 2100393..4c3a1d6 100644 --- a/src/tasks/TwitterRetweet.ts +++ b/src/tasks/TwitterRetweet.ts @@ -6,7 +6,7 @@ export default class TwitterRetweet extends ITask { static desc = 'twitter retweet' static show: boolean = true async execute(data: any) { - let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name) + const { task } = data let cfg = this.params.activity.tasks.find(t => t.id === this.constructor.name) let time = cfg.params.time; if (Date.now() - task.timeStart < time * 1000) { @@ -19,7 +19,18 @@ export default class TwitterRetweet extends ITask { task.status = TaskStatusEnum.SUCCESS task.timeFinish = Date.now() task.data = {} - await this.params.user.save() + 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 } diff --git a/src/tasks/base/ITask.ts b/src/tasks/base/ITask.ts index 13d4b42..8e84347 100644 --- a/src/tasks/base/ITask.ts +++ b/src/tasks/base/ITask.ts @@ -1,12 +1,35 @@ +import { TaskCfg } from "models/ActivityInfo" +import { TaskStatus, TaskStatusEnum } from "models/ActivityUser" +import { updateRankScore } from "services/rank.svr" export abstract class ITask { static desc: string static show: boolean = true static auto: boolean = false + params: any constructor(params: any) { // do nothing this.params = params } abstract execute(data: any): Promise + + public async claimReward(task: TaskStatus) { + const user = this.params.user + const [taskId, dateTag] = task.id.split(':'); + const cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === taskId) + await updateRankScore({ + user: user.id, + score: cfg.score, + activity: user.activity, + scoreType: taskId, + scoreParams: { + date: dateTag, + taskId: task.id + } + }) + task.status = TaskStatusEnum.CLAIMED + task.timeClaim = Date.now() + await user.save() + } } \ No newline at end of file diff --git a/src/utils/date.util.ts b/src/utils/date.util.ts new file mode 100644 index 0000000..1c6e0dd --- /dev/null +++ b/src/utils/date.util.ts @@ -0,0 +1,7 @@ +// format the date to the format we want +export const formatDate = (date: Date): string => { + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${year}${month}${day}`; +}; \ No newline at end of file