add twitter connect, accomplish other tasks

This commit is contained in:
CounterFire2023 2023-12-20 19:32:35 +08:00
parent 35c319d311
commit 5d2a3af490
10 changed files with 167 additions and 50 deletions

View File

@ -4,20 +4,23 @@
"name": "First Activity1", "name": "First Activity1",
"description": "This is the first test activity", "description": "This is the first test activity",
"tasks": [{ "tasks": [{
"task": "TwitterFollow", "task": "TwitterConnect",
"params": {} "params": {}
}, {
"task": "TwitterFollow",
"params": {"time": 6, "failRate": 60}
}, { }, {
"task": "TwitterRetweet", "task": "TwitterRetweet",
"params": {} "params": {"time": 6, "failRate": 60}
}, { }, {
"task": "DiscordJoin", "task": "DiscordJoin",
"params": {} "params": {}
}, { }, {
"task": "DiscordRole", "task": "DiscordRole",
"params": {} "params": {"time": 6, "failRate": 60}
}, { }, {
"task": "UpdateScore", "task": "UpdateScore",
"params": {} "params": {"score": [100, 20]}
}], }],
"startTime": 1702628292366, "startTime": 1702628292366,
"endTime": 1705220292366 "endTime": 1705220292366

View File

@ -2,7 +2,8 @@ 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 { all } from "deepmerge"; import { all } from "deepmerge";
import { ActivityUser, TaskStatusEnum } from "models/ActivityUser"; import { ActivityInfo } from "models/ActivityInfo";
import { ActivityUser, TaskStatus, TaskStatusEnum } from "models/ActivityUser";
import { join } from 'path' import { join } from 'path'
const fs = require('fs') const fs = require('fs')
@ -30,14 +31,16 @@ const parseCurrentTask = (user: typeof ActivityUser, task: string) => {
} }
let preEnd = true; let preEnd = true;
let currentTask = null; let currentTask = null;
let nextTask = null;
for (let taskData of user.taskProgress) { for (let taskData of user.taskProgress) {
if (taskData.id === task) { if (taskData.id === task) {
currentTask = taskData; currentTask = taskData;
break;
} }
if (taskData.status !== 2) { if (taskData.status !== 2 && !currentTask) {
preEnd = false; preEnd = false;
break; }
if (currentTask && !nextTask) {
nextTask = taskData;
} }
} }
if (!preEnd) { if (!preEnd) {
@ -46,7 +49,20 @@ const parseCurrentTask = (user: typeof ActivityUser, task: string) => {
if (!currentTask) { if (!currentTask) {
throw new ZError(13, 'task not found') throw new ZError(13, 'task not found')
} }
return {preEnd, currentTask} return {preEnd, currentTask, nextTask}
}
const parseNextTask = async (
user: typeof ActivityUser,
activity: typeof ActivityInfo,
task: TaskStatus) => {
let Task = require('../tasks/' + task.id);
if (!Task.default.auto) {
return true
}
let taskInstance = new Task.default({user, activity});
let result = await taskInstance.execute({});
return result
} }
export default class TasksController extends BaseController { export default class TasksController extends BaseController {
@ -69,7 +85,7 @@ export default class TasksController extends BaseController {
if (!user.taskProgress || user.taskProgress.length === 0) { if (!user.taskProgress || user.taskProgress.length === 0) {
for (let task of activity.tasks) { for (let task of activity.tasks) {
if (allTasks.has(task.task)) { if (allTasks.has(task.task)) {
user.taskProgress.push({id: task.task, status: 0, time: 0}) user.taskProgress.push({id: task.task, status: 0})
} }
} }
await user.save(); await user.save();
@ -94,11 +110,17 @@ export default class TasksController extends BaseController {
let user = req.user; let user = req.user;
let activity = req.activity; let activity = req.activity;
let { task } = req.params; let { task } = req.params;
let { currentTask } = parseCurrentTask(user, task); let { currentTask, nextTask } = parseCurrentTask(user, task);
if (currentTask.status !== TaskStatusEnum.RUNNING) {
throw new ZError(11, 'task not begin');
}
if (currentTask.status !== TaskStatusEnum.SUCCESS) { if (currentTask.status !== TaskStatusEnum.SUCCESS) {
let Task = require('../tasks/' + task); let Task = require('../tasks/' + task);
let taskInstance = new Task.default({user, activity}); let taskInstance = new Task.default({user, activity});
await taskInstance.check(); let result = await taskInstance.execute({});
if (result) {
await parseNextTask(user, activity, nextTask);
}
} }
return currentTask return currentTask
} }

View File

@ -26,7 +26,7 @@ export class TaskStatus {
@prop() @prop()
timeFinish: number timeFinish: number
@prop({ type: mongoose.Schema.Types.Mixed }) @prop({ type: mongoose.Schema.Types.Mixed })
params: any data: any
} }
interface ActivityUserClass extends Base, TimeStamps {} interface ActivityUserClass extends Base, TimeStamps {}
@ -34,6 +34,8 @@ interface ActivityUserClass extends Base, TimeStamps {}
@index({ address: 1, activity: 1 }, { unique: true }) @index({ address: 1, activity: 1 }, { unique: true })
@index({ inviteCode: 1, activity: 1 }, { unique: true }) @index({ inviteCode: 1, activity: 1 }, { unique: true })
@index({ inviteUser: 1, activity: 1 }, { unique: false }) @index({ inviteUser: 1, activity: 1 }, { unique: false })
@index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $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) {
@ -68,6 +70,11 @@ class ActivityUserClass extends BaseModule {
@prop() @prop()
public comment?: string public comment?: string
@prop()
public twitterId?: string
@prop()
public discordId: string
@prop() @prop()
public lastLogin?: Date public lastLogin?: Date

View File

@ -1,14 +1,35 @@
import { checkDiscord } from "services/oauth.svr";
import { ITask } from "./base/ITask"; import { ITask } from "./base/ITask";
import { ZError } from "common/ZError";
import { TaskStatusEnum } from "models/ActivityUser";
export default class DiscordJoin extends ITask { export default class DiscordJoin extends ITask {
static desc = 'join discord' static desc = 'join discord'
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
// do nothing let { address } = this.params.user
} let res = await checkDiscord(address)
async check(data: any) { console.log(res);
// do nothing 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) {
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')
}
}
return true return true
} }

View File

@ -1,13 +1,25 @@
import { ZError } from "common/ZError";
import { ITask } from "./base/ITask"; import { ITask } from "./base/ITask";
import { TaskStatusEnum } from "models/ActivityUser";
export default class DiscordRole extends ITask { export default class DiscordRole extends ITask {
static desc = 'acquire discord role' static desc = 'acquire discord role'
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
// do nothing let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name)
} let cfg = this.params.activity.tasks.find(t => t.task === this.constructor.name)
async check(data: any) { let time = cfg.params.time;
// do nothing if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'check discord role failed')
}
let num = Math.random() * 100
if (num < cfg.params.failRate) {
throw new ZError(12, 'check discord role failed')
}
task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now()
task.data = {}
await this.params.user.save()
return true return true
} }

View File

@ -0,0 +1,36 @@
import { checkTwitter } from "services/oauth.svr";
import { ITask } from "./base/ITask";
import { ActivityUser, TaskStatusEnum } from "models/ActivityUser";
import { ZError } from "common/ZError";
export default class TwitterConnect extends ITask {
static desc = 'twitter connect'
static show: boolean = true
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) {
task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now()
task.data = res.data.data
task.twitterId = res.data.data.userid
try {
await this.params.user.save()
} catch(err) {
throw new ZError(100, 'twitter already binded')
}
}
return true
}
}

View File

@ -1,31 +1,25 @@
import { checkTwitter } from "services/oauth.svr";
import { ITask } from "./base/ITask"; import { ITask } from "./base/ITask";
import { ActivityUser, TaskStatusEnum } from "models/ActivityUser"; import { TaskStatusEnum } from "models/ActivityUser";
import { ZError } from "common/ZError"; import { ZError } from "common/ZError";
export default class TwitterFollow extends ITask { export default class TwitterFollow extends ITask {
static desc = 'twitter follow' static desc = 'twitter follow'
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
// do nothing
}
async check(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) let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name)
if (res.data.data.userid && task.status !== TaskStatusEnum.SUCCESS) { let cfg = this.params.activity.tasks.find(t => t.task === this.constructor.name)
task.status = TaskStatusEnum.SUCCESS let time = cfg.params.time;
task.timeFinish = Date.now() if (Date.now() - task.timeStart < time * 1000) {
task.params = res.data.data throw new ZError(11, 'follow failed')
await this.params.user.save()
} }
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 = {}
await this.params.user.save()
return true return true
} }

View File

@ -1,13 +1,25 @@
import { ZError } from "common/ZError";
import { ITask } from "./base/ITask"; import { ITask } from "./base/ITask";
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'
static show: boolean = true static show: boolean = true
async execute(data: any) { async execute(data: any) {
// do nothing let task = this.params.user.taskProgress.find(t => t.id === this.constructor.name)
} let cfg = this.params.activity.tasks.find(t => t.task === this.constructor.name)
async check(data: any) { let time = cfg.params.time;
// do nothing if (Date.now() - task.timeStart < time * 1000) {
throw new ZError(11, 'retweet failed')
}
let num = Math.random() * 100
if (num < cfg.params.failRate) {
throw new ZError(12, 'retweet failed')
}
task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now()
task.data = {}
await this.params.user.save()
return true return true
} }

View File

@ -1,13 +1,23 @@
import { ActivityUser, TaskStatus } from "models/ActivityUser";
import { ITask } from "./base/ITask"; import { ITask } from "./base/ITask";
import { TaskCfg } from "models/ActivityInfo";
export default class UpdateScore extends ITask { export default class UpdateScore extends ITask {
static desc = 'update invite score' static desc = 'update invite score'
static show: boolean = false static show: boolean = false
static auto: boolean = true
async execute(data: any) { async execute(data: any) {
// do nothing let task = this.params.user.taskProgress.find((t: TaskStatus) => t.id === this.constructor.name)
} let cfg = this.params.activity.tasks.find((t: TaskCfg) => t.task === this.constructor.name)
async check(data: any) { let scores = cfg.params.scores;
// do nothing for (let i = 0, l = scores.length; i < l; i++) {
let score = scores[i]
let user = await ActivityUser.findById(this.params.user.inviteUser)
if (user) {
}
}
return true return true
} }

View File

@ -2,11 +2,11 @@
export abstract class ITask { export abstract class ITask {
static desc: string static desc: string
static show: boolean = true static show: boolean = true
static auto: boolean = false
params: any params: any
constructor(params: any) { constructor(params: any) {
// do nothing // do nothing
this.params = params this.params = params
} }
abstract execute(data: any): Promise<void> abstract execute(data: any): Promise<boolean>
abstract check(data: any): Promise<boolean>
} }