version 2.3

This commit is contained in:
liyunze 2024-05-25 10:07:17 +08:00
parent 81c350cf00
commit 24d58867c9
4 changed files with 298 additions and 59 deletions

View file

@ -9,6 +9,8 @@ const configs = (function () {
pointsName: "points", // string
pointsPerTask: 10, // integer
taskSeparator: [";", ","], // array of strings
openQuote: '"', // string
closeQuote: '"', // string
testTasks: false, // true or false
headerGoogleFont: true, // true: use google font, false: use system font
taskGoogleFont: true, // true: use google font, false: use system font
@ -303,22 +305,22 @@ const configs = (function () {
const responses = {
// Responses related to task addition
taskAdded: 'The {taskName}(s) "{task}" has been added, {user}!',
taskAdded: "The {taskName}(s) {task} has been added, {user}!",
noTaskAdded:
"Looks like you already hit the limit of incomplete {taskName}s, {user}",
noTaskContent:
"Try using !task the-{taskName}-you-are-working-on {user}",
duplicateTask:
"Looks like you already have the {taskName} '{message}' up there {user}!",
"Looks like you already have the {taskName} {message} up there {user}!",
taskEdited:
'{taskName} "{originalTask}" has been edited to "{task}" successfully, {user}',
'{taskName} "{originalTask}" has been edited to {task} successfully, {user}',
noTaskEdit: "Try doing !edit [index] [new task] {user}",
nowTask:
'{taskName} "{task}" is now the task you are working on, {user}!',
"{taskName} {task} is now the task you are working on, {user}!",
// Grouped by task progression responses
taskNext:
"Good job on finishing the task '{oldTask}'! Now moving onto '{newTask}', {user}!",
"Good job on finishing the task {oldTask}! Now moving onto {newTask}, {user}!",
nextNoContent: "Try using !next the-task-you-want-to-do-next {user}",
taskNextFailed:
"Unable to perform command with multiple incomplete {taskName}s, {user}!",
@ -327,7 +329,7 @@ const configs = (function () {
clearedMyDone:
"All of your completed {taskName}s have been cleared, {user}!",
taskDeleted:
'{taskName}(s) "{task}" has been deleted successfully, {user}',
"{taskName}(s) {task} has been deleted successfully, {user}",
specifyTaskIndex:
"Try specifying the index of the incomplete {taskName}(s) {user}",
clearTasksExceptBroadcaster:
@ -338,11 +340,11 @@ const configs = (function () {
// Grouped by task completion responses
taskFinished:
'Good job on finishing the {taskName}(s) "{task}", {user}! You have earned {pointCount} {pointName} and completed {doneCount} {taskName}(s) so far!',
"Good job on finishing the {taskName}(s) {task}, {user}! You have earned {pointCount} {pointName} and completed {doneCount} {taskName}(s) so far!",
allTasksFinished:
"Good job on finishing all your {taskName}s, {user}! You have completed {doneCount} {taskName}(s) so far!",
taskUnfinished:
'{taskName}(s) "{task}" has been unmarked as done, {user}!',
"{taskName}(s) {task} has been unmarked as done, {user}!",
taskAlreadyFinished:
"Looks like you already finished that {taskName} {user}",
@ -354,7 +356,7 @@ const configs = (function () {
onlyOneFocus: "You can only focus one task at a time {user}!",
specifyFocusTask:
"Try specifying an incomplete {taskName} to focus {user}!",
taskFocused: 'Task "{task}" has been focused, {user}!',
taskFocused: "Task {task} has been focused, {user}!",
// Grouped by task existence responses
noTask: "Looks like you don't have a task up there {user}",

View file

@ -5,15 +5,17 @@ const defaultConfigs = (function () {
enableLimit: false, // true or false
limit: 10, // integer
automaticDoneIndex: false, // true or false - Automatically assume first unfinished task is complete
taskName: "task", // string
taskName: "task", // string (singular)
pointsName: "points", // string
pointsPerTask: 10, // integer
taskSeparator: [";", ","], // array of strings
openQuote: '"', // string
closeQuote: '"', // string
testTasks: false, // true or false
headerGoogleFont: true, // true: use google font, false: use system font
taskGoogleFont: true, // true: use google font, false: use system font
displayTaskCount: true, // true or false
enableTaskMaster: false, // true or false
enableTaskMaster: true, // true or false
};
const animation = {
@ -303,31 +305,31 @@ const defaultConfigs = (function () {
const responses = {
// Responses related to task addition
taskAdded: 'The {taskName}(s) "{task}" has been added, {user}!',
taskAdded: "The {taskName}(s) {task} has been added, {user}!",
noTaskAdded:
"Looks like you already hit the limit of incomplete {taskName}s, {user}",
noTaskContent:
"Try using !task the-{taskName}-you-are-working-on {user}",
duplicateTask:
"Looks like you already have the {taskName} '{message}' up there {user}!",
"Looks like you already have that {taskName} up there {user}!",
taskEdited:
'{taskName} "{originalTask}" has been edited to "{task}" successfully, {user}',
'{taskName} "{originalTask}" has been edited to {task} successfully, {user}',
noTaskEdit: "Try doing !edit [index] [new task] {user}",
nowTask:
'{taskName} "{task}" is now the task you are working on, {user}!',
"{taskName} {task} is now the task you are working on, {user}!",
// Grouped by task progression responses
taskNext:
"Good job on finishing the task '{oldTask}'! Now moving onto '{newTask}', {user}!",
"Good job on finishing the task {oldTask}! Now moving onto {newTask}, {user}!",
nextNoContent: "Try using !next the-task-you-want-to-do-next {user}",
taskNextFailed:
"Unable to perform command with multiple incomplete tasks, {user}!",
"Unable to perform command with multiple incomplete {taskName}s, {user}!",
// Grouped by task deletion responses
clearedMyDone:
"All of your completed {taskName}s have been cleared, {user}!",
taskDeleted:
'{taskName}(s) "{task}" has been deleted successfully, {user}',
"{taskName}(s) {task} has been deleted successfully, {user}",
specifyTaskIndex:
"Try specifying the index of the incomplete {taskName}(s) {user}",
clearTasksExceptBroadcaster:
@ -335,15 +337,14 @@ const defaultConfigs = (function () {
clearedTasks: "All {taskName}s have been cleared, {user}!",
adminDeleteTasks: "All of {mentioned}'s {taskName}s have been deleted",
clearedAll: "All {taskName}s and points have been cleared, {user}!",
clearedDone: "All completed {taskName}s have been cleared, {user}!",
// Grouped by task completion responses
taskFinished:
'Good job on finishing the {taskName}(s) "{task}", {user}! You have earned {pointCount} {pointName} and completed {doneCount} {taskName}(s) so far!',
"Good job on finishing the {taskName}(s) {task}, {user}! You have earned {pointCount} {pointName} and completed {doneCount} {taskName}(s) so far!",
allTasksFinished:
"Good job on finishing all your {taskName}s, {user}! You have completed {doneCount} {taskName}(s) so far!",
taskUnfinished:
'{taskName}(s) "{task}" has been unmarked as done, {user}!',
"{taskName}(s) {task} has been unmarked as done, {user}!",
taskAlreadyFinished:
"Looks like you already finished that {taskName} {user}",
@ -355,7 +356,7 @@ const defaultConfigs = (function () {
onlyOneFocus: "You can only focus one task at a time {user}!",
specifyFocusTask:
"Try specifying an incomplete {taskName} to focus {user}!",
taskFocused: 'Task "{task}" has been focused, {user}!',
taskFocused: "Task {task} has been focused, {user}!",
// Grouped by task existence responses
noTask: "Looks like you don't have a task up there {user}",
@ -379,7 +380,7 @@ const defaultConfigs = (function () {
"Everyone has completed {doneCount} {taskName}(s) so far, {user}!",
noCountAll: "Looks like no one has completed a {taskName} yet {user}",
leaderboard: "Leaderboard: {leaderboard}",
setUserTaskCount: "{mentioned} now has {taskCount} {taskName}s!",
setUserTaskCount: "{mentioned} now has {taskCount} tasks!",
// Grouped by taskmaster-related responses
taskMaster: "{user} Task Master: {taskMaster} [{taskMasterCount}]",

View file

@ -1,4 +1,6 @@
var commands = configs.commands;
var openQuote = getSetting("openQuote");
var closeQuote = getSetting("closeQuote");
async function processCommand(user, command, message, flags, extra) {
params = {
@ -17,7 +19,7 @@ async function processCommand(user, command, message, flags, extra) {
}
let addedResponse = getResponse("taskAdded");
params.task = addRequest.body.task;
params.task = `${openQuote}${addRequest.body.task}${closeQuote}`;
if (addRequest.body.tasksFailedToAdd !== "") {
addedResponse += ` | Failed to add task(s): "${addRequest.body.tasksFailedToAdd}"`;
@ -35,8 +37,8 @@ async function processCommand(user, command, message, flags, extra) {
let originalTask = editRequest.body.originalTask;
let newTask = editRequest.body.newTask;
params.task = newTask;
params.originalTask = originalTask;
params.task = `${openQuote}${newTask}${closeQuote}`;
params.originalTask = `${openQuote}${originalTask}${closeQuote}`;
respond(getResponse("taskEdited"), params);
} else if (getCommand("deleteTaskCommands").includes(command)) {
@ -48,13 +50,13 @@ async function processCommand(user, command, message, flags, extra) {
}
let deletedResponse = getResponse("taskDeleted");
let removedTasks = removeRequest.body.removedTasks;
let removedTasks = `${openQuote}${removeRequest.body.removedTasks}${closeQuote}`;
let failedTasks = removeRequest.body.failedTasks;
params.task = removedTasks;
if (failedTasks !== "") {
deletedResponse += ` | Failed to delete task(s): "${failedTasks}"`;
deletedResponse += ` | Failed to delete task(s): ${openQuote}${failedTasks}${closeQuote}`;
}
respond(deletedResponse, params);
@ -69,6 +71,15 @@ async function processCommand(user, command, message, flags, extra) {
let finishedAllResponse = getResponse("allTasksFinished");
params.doneCount = await completedTasksCount(user);
finishedAllResponse = finishedAllResponse.replaceAll(
"{pointName}",
getSetting("pointsName")
);
finishedAllResponse = finishedAllResponse.replaceAll(
"{taskName}",
getSetting("taskName")
);
respond(finishedAllResponse, params);
return;
@ -82,7 +93,7 @@ async function processCommand(user, command, message, flags, extra) {
}
let finishedResponse = getResponse("taskFinished");
params.task = finishRequest.body.markedTasks;
params.task = `${openQuote}${finishRequest.body.markedTasks}${closeQuote}`;
params.doneCount = await completedTasksCount(user);
params.pointCount =
finishRequest.body.markedTasksCount * getSetting("pointsPerTask");
@ -113,7 +124,7 @@ async function processCommand(user, command, message, flags, extra) {
// task unfinished
let unfinishedResponse = getResponse("taskUnfinished");
params.task = unfinishRequest.body.markedTasks;
params.task = `${openQuote}${unfinishRequest.body.unmarkedTasks}${closeQuote}`;
if (unfinishRequest.body.failedTasks !== "") {
unfinishedResponse += ` | Failed to unfinish task(s): "${unfinishRequest.body.failedTasks}"`;
@ -124,15 +135,15 @@ async function processCommand(user, command, message, flags, extra) {
let nextRequest = await nextTask(user, message);
if (nextRequest.status !== 200) {
respond(nextRequest.body.error, params);
await respond(nextRequest.body.error, params);
return;
}
// task next
let nextResponse = getResponse("taskNext");
params.oldTask = nextRequest.body.oldTask;
params.newTask = nextRequest.body.newTask;
params.oldTask = `${openQuote}${nextRequest.body.oldTask}${closeQuote}`;
params.newTask = `${openQuote}${nextRequest.body.newTask}${closeQuote}`;
respond(nextResponse, params);
} else if (getCommand("nowTaskCommands").includes(command)) {
@ -146,7 +157,7 @@ async function processCommand(user, command, message, flags, extra) {
// task now
let nowResponse = getResponse("nowTask");
params.task = nowRequest.body.task;
params.task = `${openQuote}${nowRequest.body.task}${closeQuote}`;
respond(nowResponse, params);
} else if (getCommand("focusTaskCommands").includes(command)) {
@ -160,9 +171,9 @@ async function processCommand(user, command, message, flags, extra) {
// task focused
let focusedResponse = getResponse("taskFocused");
params.task = focusRequest.body.focusedTask;
params.task = `${openQuote}${focusRequest.body.focusedTask}${closeQuote}`;
respond(focusedResponse, params);
await respond(focusedResponse, params);
} else if (getCommand("unfocusTaskCommands").includes(command)) {
let unfocusRequest = await unfocusTask(user);
@ -287,7 +298,7 @@ async function processCommand(user, command, message, flags, extra) {
}
await clearMemory();
await clearAllExceptStreamer(configs.StreamerUsernames);
await clearAllExceptStreamer(auth.channel);
respond(getResponse("clearTasksExceptBroadcaster"), params);
} else if (getCommand("adminClearTasksCommands").includes(command)) {
if (!isStreamer(flags)) {

View file

@ -23,6 +23,8 @@ DB structure:
const styles = configs.styles;
const scrollSpeed = configs.animation.scrollSpeed;
const responses = configs.responses;
var openQuote = getSetting("openQuote");
var closeQuote = getSetting("closeQuote");
let scrolling = false;
let primaryAnimation, secondaryAnimation;
const taskSeparator = getSetting("taskSeparator");
@ -809,7 +811,8 @@ async function addTask(username, userColor, task) {
const taskExists = tasks[username].todos.find(
(t) => t.text.toLowerCase() === task.toLowerCase()
);
const taskIsInvalid = !task || !task.trim() || task.toLowerCase() === "all";
const taskIsInvalid =
!task || !task.trim() || task.toLowerCase() === "all" || isInt(task);
const userHasReachedTaskLimit =
incompleteTasksCount(username) >= getSetting("limit") &&
getSetting("enableLimit");
@ -845,7 +848,11 @@ async function addTask(username, userColor, task) {
const tasksFailedToAdd = [];
tasksToAdd.forEach((t) => {
if (t && !tasks[username].todos.find((task) => task.text === t)) {
if (
t &&
!tasks[username].todos.find((task) => task.text === t) &&
!isInt(t)
) {
tasks[username].todos.push({ text: t, done: false, focus: false });
taskListMemory.totalTaskCount++;
} else {
@ -861,8 +868,9 @@ async function addTask(username, userColor, task) {
return {
status: 200,
body: {
task: tasksToAdd.join('", "'),
tasksFailedToAdd: tasksFailedToAdd.join('", "') || "",
task: tasksToAdd.join(`${closeQuote}, ${openQuote}`),
tasksFailedToAdd:
tasksFailedToAdd.join(`${closeQuote}, ${openQuote}`) || "",
},
};
}
@ -1105,6 +1113,14 @@ async function focusTask(username, task) {
);
}
// if task is already completed, return 1
if (tasks[username].todos[index].done) {
return createErrorResponse(
`@${username} task is already completed`,
responses.specifyTaskIndex
);
}
// set all tasks to unfocused
tasks[username].todos.forEach((task) => {
task.focus = false;
@ -1267,8 +1283,10 @@ async function removeTask(username, task) {
return {
status: 200,
body: {
removedTasks: tasksRemoved.join('", "'),
failedTasks: tasksFailedToRemove.join('", "'),
removedTasks: tasksRemoved.join(`${closeQuote}, ${openQuote}`),
failedTasks: tasksFailedToRemove.join(
`${closeQuote}, ${openQuote}`
),
},
};
} else {
@ -1318,6 +1336,82 @@ async function removeTask(username, task) {
}
}
async function getFocusedTask(username) {
const tasks = await DBHandler.get("tasks");
if (!tasks[username] || tasks[username].todos.length === 0) {
return {
status: 0,
body: {
"error message": `@${username} has no tasks`,
error: getResponse("noTask"),
},
};
}
let focusedTask = tasks[username].todos.find((t) => t.focus);
if (!focusedTask) {
return {
status: 0,
body: {
"error message": `@${username} has no focused task`,
error: getResponse("noFocusedTask"),
},
};
}
return {
status: 200,
body: {
focusedTask: focusedTask.text,
},
};
}
async function checkIfTaskExists(username, task) {
const tasks = await DBHandler.get("tasks");
if (!tasks[username] || tasks[username].todos.length === 0) {
return {
status: 0,
body: {
"error message": `@${username} has no tasks`,
error: getResponse("noTask"),
},
};
}
let taskExistsBasedOnText = tasks[username].todos.find(
(t) => t.text.toLowerCase() === task.toLowerCase()
)
? true
: false;
// check if task exists based on index, has to be incomplete
let taskExistsBasedOnIndex = false;
if (tasks[username]) {
taskExistsBasedOnIndex =
isInt(task) &&
parseInt(task) > 0 &&
parseInt(task) <= tasks[username].todos.length;
}
return {
text: taskExistsBasedOnText,
index: taskExistsBasedOnIndex,
};
}
/**
* This function marks the current task of a user as complete and adds a new task to the user's task list.
*
* @param {string} username - The name of the user who is completing the current task and adding a new one.
* @param {string} task - The text description of the new task to be added.
*
* @returns {Object} - An object containing the status of the operation and a body with either the old and new task or an error message.
*/
/**
* This function marks the current task of a user as complete and adds a new task to the user's task list.
*
@ -1333,7 +1427,7 @@ async function nextTask(username, task) {
status: 0,
body: {
"error message": `@${username} has no tasks`,
error: responses.noTask,
error: getResponse("noTask"),
},
};
}
@ -1343,19 +1437,128 @@ async function nextTask(username, task) {
status: 1,
body: {
"error message": `@${username} empty task`,
error: responses.nextNoContent,
error: getResponse("nextNoContent"),
},
};
}
const incompleteTasks = tasks[username].todos.filter((t) => !t.done);
if (incompleteTasks.length !== 1) {
const taskExists = await checkIfTaskExists(username, task);
const textTaskExists = taskExists.text;
const indexTaskExists = taskExists.index;
const focusedTaskExists = incompleteTasks.find((t) => t.focus);
// check if there's a focused task
if (!focusedTaskExists && incompleteTasks.length > 1) {
return {
status: 0,
body: {
"error message": `@${username} has more than one incomplete task`,
error: responses.taskNextFailed,
"error message": `@${username} does not have a focused task`,
error: getResponse("noFocusedTask"),
},
};
} else if (textTaskExists || indexTaskExists) {
let index = tasks[username].todos.findIndex((t) => t.focus);
let oldTask = tasks[username].todos[index].text;
// if old task is the same as an existing task, return 1
if (textTaskExists) {
if (tasks[username].todos.find((t) => t.text === task)) {
return {
status: 1,
body: {
"error message": `@${username} already has this task`,
error: getResponse("duplicateTask"),
},
};
}
} else {
if (tasks[username].todos[parseInt(task) - 1].done) {
return {
status: 1,
body: {
"error message": `@${username} already finished this task`,
error: getResponse("taskAlreadyFinished"),
},
};
}
}
// mark focused task as complete then focus on the existing task
// mark task as done
tasks[username].todos[index].done = true;
tasks[username].todos[index].focus = false;
let newFocusTask = null;
// focus on the new task
if (textTaskExists) {
let newIndex = tasks[username].todos.findIndex(
(t) => t.text.toLowerCase() === task.toLowerCase()
);
newFocusTask = tasks[username].todos[newIndex];
} else {
newFocusTask = tasks[username].todos[parseInt(task) - 1];
}
newFocusTask.focus = true;
await addDoneCount(username, 1);
await DBHandler.set("tasks", tasks);
if (!scrolling) {
await renderTaskListToDOM();
}
return {
status: 200,
body: {
oldTask: oldTask,
newTask: newFocusTask.text,
},
};
} else if (focusedTaskExists) {
if (!indexTaskExists && isInt(task)) {
return {
status: 1,
body: {
"error message": `@${username} invalid input`,
error: getResponse("specifyTaskIndex"),
},
};
}
// mark the focused task as complete, then add a new task "task"
// find index of focused task
let index = tasks[username].todos.findIndex((t) => t.focus);
let oldTask = tasks[username].todos[index].text;
// mark task as done
tasks[username].todos[index].done = true;
tasks[username].todos[index].focus = false;
// add new task
tasks[username].todos.push({ text: task, done: false, focus: true });
taskListMemory.totalTaskCount++;
await addDoneCount(username, 1);
await DBHandler.set("tasks", tasks);
if (!scrolling) {
await renderTaskListToDOM();
}
return {
status: 200,
body: {
oldTask: oldTask,
newTask: task,
},
};
} else {
@ -1376,6 +1579,10 @@ async function nextTask(username, task) {
await DBHandler.set("tasks", tasks);
if (!scrolling) {
await renderTaskListToDOM();
}
return {
status: 200,
body: {
@ -1554,8 +1761,12 @@ async function markTaskDone(username, task) {
return {
status: 200,
body: {
markedTasks: tasksMarkedComplete.join('", "'),
failedTasks: tasksFailedToComplete.join('", "'),
markedTasks: tasksMarkedComplete.join(
`${closeQuote}, ${openQuote}`
),
failedTasks: tasksFailedToComplete.join(
`${closeQuote}, ${openQuote}`
),
markedTasksCount: tasksMarkedComplete.length,
},
};
@ -1756,8 +1967,12 @@ async function markTaskUndone(username, task) {
return {
status: 200,
body: {
markedTasks: tasksMarkedUndone.join('", "'),
failedTasks: tasksFailedToMarkUndone.join('", "'),
markedTasks: tasksMarkedUndone.join(
`${closeQuote}, ${openQuote}`
),
failedTasks: tasksFailedToMarkUndone.join(
`${closeQuote}, ${openQuote}`
),
},
};
} else {
@ -1919,17 +2134,27 @@ async function editTask(username, message) {
/**
* Checks the tasks of a given user. If the user has tasks, the function returns an object with a status of 200 and a formatted string of the user's tasks. If the user has no tasks, the function returns an object with a status indicating the error and an error message.
*
* @param {string} username - The name of the user whose tasks are to be checked.
* @param {string} name - The name of the user whose tasks are to be checked.
* @returns {Object} An object with a status and body. The status is 200 if the user has tasks, and the body contains a formatted string of the user's tasks. If the user has no tasks, the status indicates the error and the body contains an error message.
*/
async function checkTasks(username) {
async function checkTasks(name) {
const tasks = await DBHandler.get("tasks");
// Go through keys of tasks, find match of lowercased username
username = Object.keys(tasks).find(
(user) => user.toLowerCase() === username.toLowerCase()
let username = Object.keys(tasks).find(
(user) => user.toLowerCase() === name.toLowerCase()
);
if (!username) {
return {
status: 0,
body: {
"error message": `@${name} has no tasks`,
error: getResponse("noTask"),
},
};
}
if (!tasks[username]) {
return {
status: 0,
@ -1945,7 +2170,7 @@ async function checkTasks(username) {
const completedTasks = tasks[username].todos.filter((t) => t.done);
// format incomplete tasks into string: 1. task 1 | 2. task 2 | 3. task 3...
let reply = `incomplete {taskName}(s) (${incompleteTasks.length}) : `;
let reply = `${name}'s incomplete {taskName}s (${incompleteTasks.length}) : `;
let taskIndex;
for (let i = 0; i < incompleteTasks.length; i++) {
// get index of task by task name
@ -2115,7 +2340,7 @@ async function clearAll() {
* Clears all tasks from the local storage except for those belonging to specified streamer usernames.
* After clearing, it also clears all done tasks, cancels any ongoing animation and re-renders the task list to the DOM.
*
* @param {string[]} streamerUsernames - An array of streamer usernames whose tasks should not be cleared.
* @param {string[]} streamerUsername - An array of streamer usernames whose tasks should not be cleared.
* @returns {Object} An object with a status property indicating the success of the operation (200 for success).
*/
async function clearAllExceptStreamer(streamerUsername) {
@ -2124,7 +2349,7 @@ async function clearAllExceptStreamer(streamerUsername) {
for (const user in tasks) {
if (user.toLowerCase() === "id") continue;
if (user.toLowerCase() !== auth.channel.toLowerCase()) {
if (user.toLowerCase() !== streamerUsername.toLowerCase()) {
tasks[user].todos = [];
}
}