无视Cloudflare Cron数量限制,使用任意数量的Cron

方法比较简单,创建一个Worker每分钟触发一次,在Worker中调用其他Worker

需要使用worker binding



Cron版本

* * * * * : 分 时 天 月 周几

注意:为了简化实现,使用/进行步进的时间从0开始,如* * * */7 *会在每月的 1 8 15 22 29执行,跨月的天数会出现问题,留着以后解决(抄的库,我慢慢改

export default {
    async scheduled(event, env, ctx) {
        ctx.waitUntil(handleEvent(event, env, ctx))
    },
    async fetch(request, env, ctx) {
        return handleEvent(request, env, ctx)
    },
}

const schedules = [
    // minute hour dayOfMonth month dayOfWeek
    ['0 8,16 * * *', 'cron_1', 'https://cron'],
    ['0 8 * * *', 'cron_2', 'https://cron'],
    ['0 8 * * 6', 'cron_3', 'https://cron/'],
]

async function handleEvent(event, env, ctx) {
    const cronParser = new CronParser()
    const currentTime = new Date(new Date().toLocaleString('en-US', {timeZone: 'Asia/Shanghai'}))

    for (const [cron, action, url] of schedules) {
        const scheduleTime = await cronParser.parser(cron)
        console.log(scheduleTime)
        console.log(currentTime)
        if(
            scheduleTime.minutes.includes(currentTime.getMinutes())
            && scheduleTime.hours.includes(currentTime.getHours())
            && scheduleTime.dayOfMonth.includes(currentTime.getDate())
            && scheduleTime.month.includes(currentTime.getMonth() + 1) // month starts from 0
            && scheduleTime.dayOfWeek.includes(currentTime.getDay())
        ){
            console.log(`[INFO] ${currentTime}: Match ${action}`)
            try {
                await env[action].fetch(url)
            } catch (e) {
                console.log(e)
            }
        }
    }

    return new Response(JSON.stringify({
        'message': 'success',
        'code': 200
    }))
}

class CronParser{
    constructor(){
        const config = {
            "minutes": {
                "min": 0,
                "max": 59
            },
            "hours": {
                "min": 0,
                "max": 23
            },
            "dayOfMonth": {
                "min": 1,
                "max": 31
            },
            "month": {
                "min": 1,
                "max": 12,
                "values": {
                    "jan": 1,
                    "feb": 2,
                    "mar": 3,
                    "apr": 4,
                    "may": 5,
                    "jun": 6,
                    "jul": 7,
                    "aug": 8,
                    "sep": 9,
                    "oct": 10,
                    "nov": 11,
                    "dec": 12
                }
            },
            "dayOfWeek": {
                "min": 0,
                "max": 6,
                "values": {
                    "sun": 0,
                    "mon": 1,
                    "tue": 2,
                    "wed": 3,
                    "thu": 4,
                    "fri": 5,
                    "sat": 6
                }
            }
        }

        const getBoundries = (char, values, field, config) => {
            return new Promise((resolve, reject) => {
                if (values.length > 2 || !values[0] || !values[1])
                    return reject(`Unsupported value, ${field} -> ${values.join(char)}`)
        
                let left = !isNaN(parseInt(values[0])) ? parseInt(values[0]) : null,
                    right = !isNaN(parseInt(values[1])) ? parseInt(values[1]) : null
        
                if (!left && config.values && config.values[values[0].toLowerCase()]) {
                    left = config.values[values[0].toLowerCase()]
                }
                if (!right && config.values && config.values[values[1].toLowerCase()]) {
                    right = config.values[values[1].toLowerCase()]
                }
        
                if (
                    left === null || // cannot do !left because 0 is a valid value
                    right === null || // cannot do !right because 0 is a valid value
                    left < config.min ||
                    left > config.max ||
                    right < config.min ||
                    right > config.max ||
                    (char === '-' && left > right)
                )
                    return reject(`Unsupported value, ${field} -> ${values.join(char)}`)
        
                return resolve({ left, right })
            })
        }

        this.parser = async (expression) => {
            /*
            Step-1: '1-10/5 10-13 ? * WED,FRI' becomes ['1-10/5', '10-13', '?', '*', 'WED,FRI']
            */
            expression = expression.split(' ')
        
            if (expression.length !== 5) return 'Unsupported expression. Exactly 5 time fields expected.'
            else if (expression[2] === '?' && expression[4] === '?')
                return 'Both dayOfMonth and dayOfWeek cannot be "?"'
        
            const configKeys = Object.keys(config),
                result = {
                    minutes: [],
                    hours: [],
                    dayOfMonth: [],
                    month: [],
                    dayOfWeek: []
                }
        
            for (let i = 0; i < 5; i++) {
                /*
                Step-2: '1-10/5' becomes ['1-10', '5']
                */
                expression[i] = expression[i].split('/')
        
                if (expression[i][0] === '*' || expression[i][0] === '?') {
                    for (let j = config[configKeys[i]].min; j <= config[configKeys[i]].max; j += +expression[i][1] || 1) {
                        // incrementing by steps value if available
                        result[configKeys[i]].push(j)
                    }
                } else if (expression[i][0].indexOf(',') !== -1) {
                    try {
                        const { left, right } = await getBoundries(
                            ',',
                            expression[i][0].split(','),
                            configKeys[i],
                            config[configKeys[i]]
                        )
        
                        result[configKeys[i]].push(left)
                        result[configKeys[i]].push(right)
                    } catch (err) {
                        return err
                    }
                } else if (expression[i][0].indexOf('-') !== -1) {
                    try {
                        const { left, right } = await getBoundries(
                            '-',
                            expression[i][0].split('-'),
                            configKeys[i],
                            config[configKeys[i]]
                        )
        
                        for (let j = left; j <= right; j++) {
                            result[configKeys[i]].push(j)
                        }
                    } catch (err) {
                        return err
                    }
                } else {
                    let value = !isNaN(parseInt(expression[i][0])) ? parseInt(expression[i][0]) : null
        
                    if (
                        !value &&
                        config[configKeys[i]].values &&
                        config[configKeys[i]].values[expression[i][0].toLowerCase()]
                    )
                        value = config[configKeys[i]].values[expression[i][0].toLowerCase()]
        
                    if (
                        value === null || // cannot do !value because 0 is a valid value
                        value < config[configKeys[i]].min ||
                        value > config[configKeys[i]].max
                    )
                        return `Unsupported value, ${configKeys[i]} -> ${expression[i][0]}`
        
                    result[configKeys[i]].push(value)
                    if (expression[i][1]) result[configKeys[i]].push(value + +expression[i][1]) // adding steps value  if available
                }
            }
        
            return result
        }
    }
}

数组版本,只实现了时分,其它的比如年月可以自行实现.

export default {
    async scheduled(event, env, ctx) {
        ctx.waitUntil(handleEvent(event, env, ctx))
    },
    async fetch(request, env, ctx) {
        return handleEvent(request, env, ctx)
    },
}

const schedule = [
    [[[8, 0], [18, 0]], 'cron_1', 'https://cron'],
    [[[8, 0]], 'cron_2', 'https://cron'],
    [[[8, 0]], 'log', 'https://cron/clear'],
]

async function handleEvent(event, env, ctx) {
    const currentTime = new Date(new Date().toLocaleString('en-US', {timeZone: 'Asia/Shanghai'}))
    const currentHour = currentTime.getHours()
    const currentMinute = currentTime.getMinutes()

    for (const [times, action, url] of schedule) {
        for (const [hour, minute] of times) {
            if (currentHour === hour && currentMinute === minute) {
                console.log(`[INFO] ${currentTime}: Match ${action}`)
                try {
                    await env[action].fetch(url)
                } catch (e) {
                    console.log(e)
                }
            }
        }
    }

    return new Response(JSON.stringify({
        'message': 'success',
        'code': 200
    }))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值