mirror of
https://gitea.com/Lydanne/issues-helper.git
synced 2025-08-20 02:35:58 +08:00
wip: code review
This commit is contained in:
@@ -1,14 +1,20 @@
|
||||
import { dealStringToArr } from 'actions-util';
|
||||
import { dealStringToArr, checkPermission, TPermissionType } from 'actions-util';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||
import * as core from '../core';
|
||||
import { TIssueState, TUpdateMode, TEmoji, TLockReasons } from '../types';
|
||||
import { ELockReasons } from '../shared';
|
||||
import { IIssueCoreEngine, IListIssuesParams, TListIssuesResults } from '../issue';
|
||||
import { matchKeyword, checkDuplicate, replaceStr2Arr } from '../util';
|
||||
import { TIssueState, TOutList, TEmoji } from '../types';
|
||||
import { IIssueCoreEngine, IListIssuesParams, TIssueList, TCommentInfo } from '../issue';
|
||||
import {
|
||||
doAddAssignees,
|
||||
doAddLabels,
|
||||
doCreateComment,
|
||||
doCreateCommentEmoji,
|
||||
doCloseIssue,
|
||||
doLockIssue,
|
||||
doUpdateComment,
|
||||
doSetLabels,
|
||||
} from './base';
|
||||
|
||||
let ICE: IIssueCoreEngine;
|
||||
@@ -16,7 +22,7 @@ export function initAdvancedICE(_ICE: IIssueCoreEngine) {
|
||||
ICE = _ICE;
|
||||
}
|
||||
|
||||
export async function doQueryIssues(state: TIssueState | 'all', creator?: string): Promise<TListIssuesResults> {
|
||||
export async function doQueryIssues(state: TIssueState | 'all', creator?: string, ignoreLabels?: boolean): Promise<TIssueList> {
|
||||
const params = {
|
||||
state,
|
||||
} as IListIssuesParams;
|
||||
@@ -29,12 +35,12 @@ export async function doQueryIssues(state: TIssueState | 'all', creator?: string
|
||||
issueMentioned ? (params.mentioned = issueMentioned) : null;
|
||||
|
||||
const labels = core.getInput('labels');
|
||||
labels ? params.labels = labels : null;
|
||||
labels && !ignoreLabels ? params.labels = labels : null;
|
||||
|
||||
creator? params.creator = creator : null;
|
||||
creator ? params.creator = creator : null;
|
||||
|
||||
const issuesList = await ICE.listIssues(params);
|
||||
const issues: TListIssuesResults = [];
|
||||
const issues: TIssueList = [];
|
||||
const issueNumbers: number[] = [];
|
||||
|
||||
if (issuesList.length) {
|
||||
@@ -91,8 +97,9 @@ export async function doCheckInactive(body: string, emoji?: string) {
|
||||
const inactiveLabel = core.getInput('inactive-label') || 'inactive';
|
||||
for (const issue of issues) {
|
||||
const { labels, number } = issue;
|
||||
const labelNames = labels.map(({name}) => name);
|
||||
const labelNames = labels.map(({ name }) => name);
|
||||
if (!labelNames.includes(inactiveLabel)) {
|
||||
core.info(`[doCheckInactive] Doing ---> ${number}`);
|
||||
await doAddLabels([inactiveLabel], number);
|
||||
if (body) await doCreateComment(body, emoji, number);
|
||||
} else {
|
||||
@@ -103,3 +110,259 @@ export async function doCheckInactive(body: string, emoji?: string) {
|
||||
core.info(`[doCheckInactive] Query issues empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 issue 是否满足条件,满足返回 true
|
||||
* 当前 issue 的指定人是否有一个满足 assigneeIncludes 里的某个
|
||||
* 关键字匹配,是否包含前一个某个+后一个某个 '官网,网站/挂了,无法访问'
|
||||
*/
|
||||
export async function doCheckIssue() {
|
||||
let checkResult = true;
|
||||
|
||||
const issue = await ICE.getIssue();
|
||||
const assigneeIncludes = core.getInput('assignee-includes');
|
||||
|
||||
if (assigneeIncludes) {
|
||||
const assigneesCheck = dealStringToArr(assigneeIncludes);
|
||||
let checkAssignee = false;
|
||||
issue.assignees.forEach(it => {
|
||||
if (checkResult && !checkAssignee && assigneesCheck.includes(it.login)) {
|
||||
checkResult = true;
|
||||
checkAssignee = true;
|
||||
}
|
||||
});
|
||||
!checkAssignee ? (checkResult = false) : null;
|
||||
}
|
||||
|
||||
const titleRemove = core.getInput('title-excludes');
|
||||
if (!!checkResult && titleRemove) {
|
||||
const removes = dealStringToArr(titleRemove);
|
||||
let t = issue.title;
|
||||
removes.forEach(re => {
|
||||
t = t.replace(re, '');
|
||||
});
|
||||
if (t.trim().length == 0) {
|
||||
checkResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
const titleIncludes = core.getInput('title-includes');
|
||||
if (!!checkResult && titleIncludes) {
|
||||
const titleArr = titleIncludes.split('/');
|
||||
const keyword1 = dealStringToArr(titleArr[0]);
|
||||
const keyword2 = dealStringToArr(titleArr[1]);
|
||||
checkResult = keyword2.length
|
||||
? matchKeyword(issue.title, keyword1) && matchKeyword(issue.title, keyword2)
|
||||
: matchKeyword(issue.title, keyword1);
|
||||
}
|
||||
|
||||
const bodyIncludes = core.getInput('body-includes');
|
||||
if (!!checkResult && bodyIncludes) {
|
||||
const bodyArr = bodyIncludes.split('/');
|
||||
const keyword1 = dealStringToArr(bodyArr[0]);
|
||||
const keyword2 = dealStringToArr(bodyArr[1]);
|
||||
checkResult = keyword2.length
|
||||
? matchKeyword(issue.body, keyword1) && matchKeyword(issue.body, keyword2)
|
||||
: matchKeyword(issue.body, keyword1);
|
||||
}
|
||||
|
||||
core.info(`[doCheckIssue] result is [${checkResult}]`);
|
||||
core.setOutput('check-result', checkResult);
|
||||
}
|
||||
|
||||
export async function doCloseIssues(body: string, emoji?: string) {
|
||||
const issues = await doQueryIssues('open');
|
||||
if (issues.length) {
|
||||
for (const { number } of issues) {
|
||||
core.info(`[doCloseIssues] Doing ---> ${number}`);
|
||||
if (body) await doCreateComment(body, emoji, number);
|
||||
await doCloseIssue(number);
|
||||
}
|
||||
} else {
|
||||
core.info(`[doCloseIssues] Query issues empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function doFindComments() {
|
||||
const commentList = await ICE.listComments();
|
||||
core.info(`[doFindComments] success!`);
|
||||
|
||||
const comments: TOutList = [];
|
||||
|
||||
if (commentList.length) {
|
||||
const commentAuth = core.getInput('comment-auth');
|
||||
const bodyIncludes = core.getInput('body-includes');
|
||||
const direction = core.getInput('direction') === 'desc' ? 'desc' : 'asc';
|
||||
for (const comment of commentList) {
|
||||
const checkUser = commentAuth ? comment.user.login === commentAuth : true;
|
||||
const checkBody = bodyIncludes ? comment.body.includes(bodyIncludes) : true;
|
||||
if (checkUser && checkBody) {
|
||||
comments.push({
|
||||
id: comment.id,
|
||||
auth: comment.user.login,
|
||||
body: comment.body,
|
||||
created: comment.created_at,
|
||||
updated: comment.updated_at,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (direction === 'desc') {
|
||||
comments.reverse();
|
||||
}
|
||||
core.setOutput('comments', comments);
|
||||
core.info(`[doFindComments] comments --> ${JSON.stringify(comments)}`);
|
||||
} else {
|
||||
core.info(`[doFindComments] Query comments empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function doFindIssues() {
|
||||
let issueState = core.getInput('issue-state');
|
||||
if (issueState !== 'all' && issueState !== 'closed') {
|
||||
issueState = 'open';
|
||||
}
|
||||
const issueList = await doQueryIssues(issueState as TIssueState | 'all');
|
||||
let issues: TOutList = [];
|
||||
if (issueList.length) {
|
||||
const direction = core.getInput('direction') === 'desc' ? 'desc' : 'asc';
|
||||
issues = issueList.map(issue => {
|
||||
return {
|
||||
auth: issue.user.login,
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
body: issue.body,
|
||||
state: issue.state,
|
||||
created: issue.created_at,
|
||||
updated: issue.updated_at,
|
||||
}
|
||||
})
|
||||
if (direction === 'desc') {
|
||||
issues.reverse();
|
||||
}
|
||||
core.info(`[doFindIssues] issues --> ${JSON.stringify(issues)}`);
|
||||
} else {
|
||||
core.info(`[doFindIssues] Query issues empty!`);
|
||||
}
|
||||
core.setOutput('issues', issues);
|
||||
}
|
||||
|
||||
export async function doLockIssues(body: string, emoji?: string) {
|
||||
let issueState = core.getInput('issue-state');
|
||||
if (issueState !== 'all' && issueState !== 'closed') {
|
||||
issueState = 'open';
|
||||
}
|
||||
const issues = await doQueryIssues(issueState as TIssueState | 'all');
|
||||
|
||||
if (issues.length) {
|
||||
for (const { number } of issues) {
|
||||
core.info(`[doLockIssues] Doing ---> ${number}`);
|
||||
if (body) await doCreateComment(body, emoji, number);
|
||||
await doLockIssue(number);
|
||||
}
|
||||
} else {
|
||||
core.info(`[doLockIssues] Query issues empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function doMarkAssignees(comment: TCommentInfo) {
|
||||
const assignCommand = core.getInput('assign-command') || '/assign';
|
||||
if (comment.body.startsWith(assignCommand)) {
|
||||
const { body, user } = comment;
|
||||
const assigns = replaceStr2Arr(body, assignCommand, '@');
|
||||
const requirePermission = core.getInput('require-permission') || 'write';
|
||||
const permission = await ICE.getUserPermission(user.login);
|
||||
if (!checkPermission(requirePermission as TPermissionType, permission)) {
|
||||
core.info(`[doMarkAssignees] The user ${user.login} is not allow!`);
|
||||
return;
|
||||
}
|
||||
await doAddAssignees(assigns);
|
||||
core.info(`[doMarkAssignees] Done!`);
|
||||
} else {
|
||||
core.info(`[doMarkAssignees] The issues ignore!`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function doMarkDuplicate(comment: TCommentInfo, labels?: string[] | void, emoji?: string) {
|
||||
const duplicateCommand = core.getInput('duplicate-command');
|
||||
const duplicateLabels = core.getInput('duplicate-labels');
|
||||
const removeLables = core.getInput('remove-labels') || '';
|
||||
const closeIssue = core.getInput('close-issue');
|
||||
const requirePermission = core.getInput('require-permission') || 'write';
|
||||
|
||||
const commentId = comment.id;
|
||||
const commentBody = comment.body;
|
||||
const commentUser = comment.user.login;
|
||||
|
||||
const ifCommandInput = !!duplicateCommand;
|
||||
|
||||
if (
|
||||
!commentBody.includes('?') &&
|
||||
((ifCommandInput &&
|
||||
commentBody.startsWith(duplicateCommand) &&
|
||||
commentBody.split(' ')[0] == duplicateCommand) ||
|
||||
checkDuplicate(commentBody))
|
||||
) {
|
||||
const permission = await ICE.getUserPermission(commentUser);
|
||||
if (!checkPermission(requirePermission as TPermissionType, permission)) {
|
||||
core.info(`[doMarkDuplicate] The user ${commentUser} is not allow!`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ifCommandInput) {
|
||||
const nextBody = commentBody.replace(duplicateCommand, 'Duplicate of');
|
||||
await doUpdateComment(commentId, nextBody, 'replace', emoji);
|
||||
} else if (emoji) {
|
||||
await doCreateCommentEmoji(commentId, emoji);
|
||||
}
|
||||
|
||||
const issue = await ICE.getIssue();
|
||||
let newLabels: string[] = [];
|
||||
if (issue.labels.length > 0) {
|
||||
newLabels = issue.labels
|
||||
.map(({ name }) => name)
|
||||
.filter(name => !dealStringToArr(removeLables).includes(name));
|
||||
}
|
||||
if (duplicateLabels) {
|
||||
newLabels = [...newLabels, ...dealStringToArr(duplicateLabels)];
|
||||
}
|
||||
if (labels) {
|
||||
newLabels = [...labels];
|
||||
}
|
||||
if (newLabels.length > 0) {
|
||||
await doSetLabels(newLabels);
|
||||
}
|
||||
if (closeIssue === 'true') {
|
||||
await doCloseIssue();
|
||||
}
|
||||
core.info(`[doMarkDuplicate] Done!`);
|
||||
} else {
|
||||
core.warning(
|
||||
`This comment body should start whith 'duplicate-command' or 'Duplicate of' and not include '?'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function doWelcome(auth: string, issueNumber: number, body: string, labels?: string[] | void, assignees?: string[] | void, emoji?: string) {
|
||||
core.info(`[doWelcome] [${auth}]`);
|
||||
const issues = await doQueryIssues('all', auth, true);
|
||||
if (issues.length == 0 || (issues.length == 1 && issues[0].number == issueNumber)) {
|
||||
if (body) {
|
||||
await doCreateComment(body, emoji);
|
||||
}
|
||||
|
||||
if (assignees?.length) {
|
||||
await doAddAssignees(assignees);
|
||||
}
|
||||
|
||||
if (labels?.length) {
|
||||
await doAddLabels(labels);
|
||||
}
|
||||
|
||||
const issueEmoji = core.getInput('issue-emoji');
|
||||
if (issueEmoji) {
|
||||
await ICE.createIssueEmoji(dealStringToArr(issueEmoji) as TEmoji[]);
|
||||
}
|
||||
} else {
|
||||
core.info(`[doWelcome] ${auth} is not first time!`);
|
||||
}
|
||||
}
|
||||
|
@@ -20,9 +20,10 @@ export async function doAddLabels(labels: string[], issueNumber?: number) {
|
||||
core.info(`[doAddLabels] [${labels}] success!`);
|
||||
}
|
||||
|
||||
export async function doCloseIssue(issueNumber: number) {
|
||||
export async function doCloseIssue(issueNumber?: number) {
|
||||
if (issueNumber) ICE.setIssueNumber(issueNumber);
|
||||
await ICE.closeIssue();
|
||||
core.info(`[doCloseIssue] [${issueNumber}] success!`);
|
||||
core.info(`[doCloseIssue] success!`);
|
||||
}
|
||||
|
||||
export async function doCreateComment(body: string, emoji?: string, issueNumber?: number) {
|
||||
@@ -87,20 +88,20 @@ export async function doDeleteComment(_commentId: number | void) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function doLockIssue(issueNumber: number) {
|
||||
const lockReason = core.getInput('lock-reason') || '';
|
||||
// @ts-ignore
|
||||
export async function doLockIssue(issueNumber?: number) {
|
||||
if (issueNumber) ICE.setIssueNumber(issueNumber);
|
||||
const lockReason = (core.getInput('lock-reason') || '') as TLockReasons;
|
||||
if (lockReason && !ELockReasons[lockReason]) {
|
||||
core.warning(`[doLockIssue] lock-reason is illegal!`);
|
||||
return;
|
||||
}
|
||||
await ICE.lockIssue(lockReason as TLockReasons);
|
||||
core.info(`[doLockIssue] ${issueNumber} success!`);
|
||||
core.info(`[doLockIssue] success!`);
|
||||
}
|
||||
|
||||
export async function doOpenIssue(issueNumber: number) {
|
||||
export async function doOpenIssue() {
|
||||
await ICE.openIssue();
|
||||
core.info(`[doOpenIssue] [${issueNumber}] success!`);
|
||||
core.info(`[doOpenIssue] success!`);
|
||||
}
|
||||
|
||||
export async function doRemoveAssignees(assignees: string[]) {
|
||||
@@ -118,9 +119,9 @@ export async function doSetLabels(labels: string[]) {
|
||||
core.info(`[doSetLabels] [${labels}] success!`);
|
||||
}
|
||||
|
||||
export async function doUnlockIssue(issueNumber: number) {
|
||||
export async function doUnlockIssue() {
|
||||
await ICE.unlockIssue();
|
||||
core.info(`[doUnlockIssue] [${issueNumber}] success!`);
|
||||
core.info(`[doUnlockIssue] success!`);
|
||||
}
|
||||
|
||||
export async function doUpdateComment(_commentId: number | void, body: string, updateMode: TUpdateMode, emoji: string | void) {
|
||||
@@ -137,6 +138,7 @@ export async function doUpdateComment(_commentId: number | void, body: string, u
|
||||
}
|
||||
|
||||
export async function doUpdateIssue(issueNumber: number, state: TIssueState, title: string | void, body: string | void, updateMode: TUpdateMode, labels?: string[] | void, assignees?: string[] | void) {
|
||||
if (issueNumber) ICE.setIssueNumber(issueNumber);
|
||||
await ICE.updateIssue(state, title, body, updateMode, labels, assignees);
|
||||
core.info(`[doUpdateIssue] [${issueNumber}] success!`);
|
||||
core.info(`[doUpdateIssue] success!`);
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import { Context, TIssueState, TUpdateMode, TAction, TEmoji } from '../types';
|
||||
import {
|
||||
IssueCoreEngine,
|
||||
IIssueCoreEngine,
|
||||
TCommentInfo,
|
||||
} from '../issue';
|
||||
import { dealRandomAssignees } from '../util';
|
||||
import { IIssueHelperEngine } from './types';
|
||||
@@ -32,6 +33,14 @@ import {
|
||||
import {
|
||||
initAdvancedICE,
|
||||
doCheckInactive,
|
||||
doCheckIssue,
|
||||
doCloseIssues,
|
||||
doFindComments,
|
||||
doFindIssues,
|
||||
doLockIssues,
|
||||
doMarkAssignees,
|
||||
doMarkDuplicate,
|
||||
doWelcome,
|
||||
} from './advanced';
|
||||
|
||||
export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
@@ -106,7 +115,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
}
|
||||
|
||||
public async doExeAction(action: TAction) {
|
||||
const { owner, repo, issueNumber, emoji, labels, assignees, title, body, updateMode, state } = this;
|
||||
const { owner, repo, issueNumber, emoji, labels, assignees, title, body, updateMode, state, ctx } = this;
|
||||
switch (action) {
|
||||
// ---[ Base Begin ]--->>>
|
||||
case 'add-assignees': {
|
||||
@@ -126,7 +135,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
break;
|
||||
}
|
||||
case 'close-issue': {
|
||||
await doCloseIssue(issueNumber);
|
||||
await doCloseIssue();
|
||||
break;
|
||||
}
|
||||
case 'create-comment': {
|
||||
@@ -146,11 +155,11 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
break;
|
||||
}
|
||||
case 'lock-issue': {
|
||||
await doLockIssue(issueNumber);
|
||||
await doLockIssue();
|
||||
break;
|
||||
}
|
||||
case 'open-issue': {
|
||||
await doOpenIssue(issueNumber);
|
||||
await doOpenIssue();
|
||||
break;
|
||||
}
|
||||
case 'remove-assignees': {
|
||||
@@ -178,7 +187,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
break;
|
||||
}
|
||||
case 'unlock-issue': {
|
||||
await doUnlockIssue(issueNumber);
|
||||
await doUnlockIssue();
|
||||
break;
|
||||
}
|
||||
case 'update-comment': {
|
||||
@@ -186,7 +195,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
break;
|
||||
}
|
||||
case 'update-issue': {
|
||||
await doUpdateIssue(issueNumber, state, title, body, updateMode, labels, assignees);
|
||||
await doUpdateIssue(0, state, title, body, updateMode, labels, assignees);
|
||||
break;
|
||||
}
|
||||
// ---[ Base End ]--->>>
|
||||
@@ -196,6 +205,59 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
||||
await doCheckInactive(body, emoji);
|
||||
break;
|
||||
}
|
||||
case 'check-issue': {
|
||||
await doCheckIssue();
|
||||
break;
|
||||
}
|
||||
case 'close-issues': {
|
||||
await doCloseIssues(body, emoji);
|
||||
break;
|
||||
}
|
||||
case 'find-comments': {
|
||||
await doFindComments();
|
||||
break;
|
||||
}
|
||||
case 'find-issues': {
|
||||
await doFindIssues();
|
||||
break;
|
||||
}
|
||||
case 'lock-issues': {
|
||||
await doLockIssues(body, emoji);
|
||||
break;
|
||||
}
|
||||
case 'mark-assignees': {
|
||||
if (this.checkEvent4Mark()) {
|
||||
core.warning(`[mark-assignees] only support event '[issue_comment: created/edited]'!`);
|
||||
return;
|
||||
}
|
||||
await doMarkAssignees(ctx.payload.comment as TCommentInfo);
|
||||
break;
|
||||
}
|
||||
case 'mark-duplicate': {
|
||||
if (this.checkEvent4Mark()) {
|
||||
core.warning(`[mark-duplicate] only support event '[issue_comment: created/edited]'!`);
|
||||
return;
|
||||
}
|
||||
await doMarkDuplicate(ctx.payload.comment as TCommentInfo, labels, emoji);
|
||||
break;
|
||||
}
|
||||
case 'welcome': {
|
||||
if (ctx.eventName === 'issues' && ctx.payload.action === 'opened') {
|
||||
await doWelcome(ctx.actor, issueNumber, body, labels, assignees, emoji);
|
||||
} else {
|
||||
core.warning('[welcome] only support issue opened!');
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
core.warning(`The ${action} is not allowed.`)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private checkEvent4Mark() {
|
||||
const { ctx } = this;
|
||||
return ctx.eventName !== 'issue_comment' && (ctx.payload.action === 'created' || ctx.payload.action === 'edited');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user