diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..9ab5017 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,3 @@ +const NAMESPACE_NAME = 'CronJobsTimestamp'; + +export { NAMESPACE_NAME }; diff --git a/src/handlers/scheduledEventHandler.ts b/src/handlers/scheduledEventHandler.ts index 290112e..476c8f2 100644 --- a/src/handlers/scheduledEventHandler.ts +++ b/src/handlers/scheduledEventHandler.ts @@ -1,8 +1,66 @@ +import { KVNamespace } from '@cloudflare/workers-types'; + import { handleConfig } from '../config/config'; -import { env } from '../types/global.types'; +import { NAMESPACE_NAME } from '../constants'; +import { env, NicknameUpdateResponseType } from '../types/global.types'; +import { generateJwt } from '../utils/generateJwt'; export async function ping(env: env) { const url = handleConfig(env); const response = await fetch(`${url.baseUrl}/healthcheck`); return response; } + +export async function callDiscordNicknameBatchUpdate(env: env) { + const namespace = env[NAMESPACE_NAME] as unknown as KVNamespace; + let lastNicknameUpdate: string | null = '0'; + try { + lastNicknameUpdate = await namespace.get('DISCORD_NICKNAME_UPDATED_TIME'); + if (lastNicknameUpdate === null) { + throw new Error('Error while fetching KV "DISCORD_NICKNAME_UPDATED_TIME" timestamp'); + } + if (!lastNicknameUpdate) { + lastNicknameUpdate = '0'; + } + } catch (err) { + console.error(err, 'Error while fetching the timestamp for last nickname update'); + throw err; + } + + const url = handleConfig(env); + let token; + try { + token = await generateJwt(env); + } catch (err) { + console.error(`Error while generating JWT token: ${err}`); + throw err; + } + const response = await fetch(`${url.baseUrl}/discord-actions/nickname/status`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + lastNicknameUpdate, + }), + }); + if (!response.ok) { + throw new Error("Error while trying to update users' discord nickname"); + } + + const data: NicknameUpdateResponseType = await response.json(); + if (data?.data.totalUsersStatus !== 0 && data?.data.successfulNicknameUpdates === 0) { + throw new Error("Error while trying to update users' discord nickname"); + } + + console.log(data); + + try { + await namespace.put('DISCORD_NICKNAME_UPDATED_TIME', Date.now().toString()); + } catch (err) { + console.error('Error while trying to update the last nickname change timestamp'); + } + + return data; +} diff --git a/src/types/global.types.ts b/src/types/global.types.ts index 89578cd..69dcd12 100644 --- a/src/types/global.types.ts +++ b/src/types/global.types.ts @@ -1,3 +1,12 @@ export type env = { [key: string]: string; }; + +export type NicknameUpdateResponseType = { + message: string; + data: { + totalUsersStatus: number; + successfulNicknameUpdates: number; + unsuccessfulNicknameUpdates: number; + }; +}; diff --git a/src/worker.ts b/src/worker.ts index e90e029..219fd47 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,12 +1,20 @@ -import { ping } from './handlers/scheduledEventHandler'; +import { callDiscordNicknameBatchUpdate, ping } from './handlers/scheduledEventHandler'; import { env } from './types/global.types'; +const EVERY_4_HOURS = '0 */4 * * *'; +const EVERY_6_HOURS = '0 */6 * * *'; + export default { - // We need to keep all 3 parameters in this format even if they are not used as as cloudflare workers need them to be present So we are disabling eslint rule of no-unused-vars - // for more details read here: https://community.cloudflare.com/t/waituntil-is-not-a-function-when-using-workers-with-modules/375781/4 - // eslint-disable-next-line no-unused-vars - async scheduled(req: Request, env: env, ctx: ExecutionContext) { - ctx.waitUntil(ping(env)); + async scheduled(req: ScheduledController, env: env, ctx: ExecutionContext) { + switch (req.cron) { + case EVERY_4_HOURS: + ctx.waitUntil(ping(env)); + break; + case EVERY_6_HOURS: + return await callDiscordNicknameBatchUpdate(env); + default: + console.error('Unknown Trigger Value!'); + } }, // We need to keep all 3 parameters in this format even if they are not used as as cloudflare workers need them to be present So we are disabling eslint rule of no-unused-vars // for more details read here: https://community.cloudflare.com/t/waituntil-is-not-a-function-when-using-workers-with-modules/375781/4 diff --git a/wrangler.toml b/wrangler.toml index c6455e0..dd66e15 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,13 +1,24 @@ name = "cron-jobs" main = "src/worker.ts" compatibility_date = "2023-07-17" -[triggers] -crons = ["0 */4 * * *"] -# # KV Namespace binding - For more information: https://developers.cloudflare.com/workers/runtime-apis/kv -# [[kv_namespaces]] -# binding = "MY_KV_NAMESPACE" -# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +kv_namespaces = [ + { binding = "CronJobsTimestamp", id = "7dcf94601bec415db4b13d5d1faf0fc3" } +] + +# [env.staging] +# the BINDING_NAME must be CronJobsTimestamp to override in the staging env +# kv_namespaces = [ +# { binding = "", id = "" } +# ] + +[env.production] +kv_namespaces = [ + { binding = "CronJobsTimestamp", id = "3a10f726c95d4afea9dee5fd00f029b9" } +] + +[triggers] +crons = ["0 */4 * * *", "0 */6 * * *"] # # Durable Object binding - For more information: https://developers.cloudflare.com/workers/runtime-apis/durable-objects # [[durable_objects]]