From 9a5ba2c19f5f63e64edc05108b2890beeb2a8ed9 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Mon, 14 Oct 2024 20:12:00 -0400 Subject: [PATCH] added telegram notifier webhook --- config.json.example | 1 + .../{discordNotifier.ts => discord.ts} | 0 src/webhooks/webhooks/index.ts | 6 +- src/webhooks/webhooks/telegram.ts | 92 +++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) rename src/webhooks/webhooks/{discordNotifier.ts => discord.ts} (100%) create mode 100644 src/webhooks/webhooks/telegram.ts diff --git a/config.json.example b/config.json.example index 66d2ae95..b2fd12ac 100644 --- a/config.json.example +++ b/config.json.example @@ -40,6 +40,7 @@ }, "daoDaoBase": "https://testnet.daodao.zone", "discordNotifierApiKey": "key", + "telegramNotifierApiKey": "key", "inboxSecret": "secret", "notifierSecret": "secret", "websocketsSecret": "secret", diff --git a/src/webhooks/webhooks/discordNotifier.ts b/src/webhooks/webhooks/discord.ts similarity index 100% rename from src/webhooks/webhooks/discordNotifier.ts rename to src/webhooks/webhooks/discord.ts diff --git a/src/webhooks/webhooks/index.ts b/src/webhooks/webhooks/index.ts index 983fcad3..1377e149 100644 --- a/src/webhooks/webhooks/index.ts +++ b/src/webhooks/webhooks/index.ts @@ -4,9 +4,10 @@ import { State, WasmStateEvent } from '@/db' import { WasmCodeService } from '@/services/wasm-codes' import { Config, ProcessedWebhook, Webhook, WebhookMaker } from '@/types' -import * as discordNotifier from './discordNotifier' +import * as discord from './discord' import * as indexerCwReceipt from './indexerCwReceipt' import * as notify from './notify' +import * as telegram from './telegram' import * as websockets from './websockets' let processedWebhooks: ProcessedWebhook[] | undefined @@ -17,7 +18,8 @@ export const getProcessedWebhooks = ( if (!processedWebhooks) { const webhookMakers: WebhookMaker[] = [ // Add webhook makers here. - ...Object.values(discordNotifier), + ...Object.values(discord), + ...Object.values(telegram), ...Object.values(indexerCwReceipt), ...Object.values(notify), ...Object.values(websockets), diff --git a/src/webhooks/webhooks/telegram.ts b/src/webhooks/webhooks/telegram.ts new file mode 100644 index 00000000..8f973291 --- /dev/null +++ b/src/webhooks/webhooks/telegram.ts @@ -0,0 +1,92 @@ +import { WasmStateEvent } from '@/db' +import { + activeProposalModules, + config as daoCoreConfig, +} from '@/formulas/formulas/contract/daoCore/base' +import { StatusEnum } from '@/formulas/formulas/contract/proposal/types' +import { WebhookMaker, WebhookType } from '@/types' +import { dbKeyForKeys, dbKeyToKeys } from '@/utils' + +import { getDaoAddressForProposalModule } from '../utils' + +const CODE_IDS_KEYS = ['dao-proposal-single', 'dao-proposal-multiple'] + +const KEY_PREFIX_PROPOSALS = dbKeyForKeys('proposals', '') +const KEY_PREFIX_PROPOSALS_V2 = dbKeyForKeys('proposals_v2', '') + +// Fire webhook when a proposal is created. +export const makeProposalCreated: WebhookMaker = ( + config, + state +) => ({ + filter: { + EventType: WasmStateEvent, + codeIdsKeys: CODE_IDS_KEYS, + matches: (event) => + // Starts with proposals or proposals_v2. + (event.key.startsWith(KEY_PREFIX_PROPOSALS) || + event.key.startsWith(KEY_PREFIX_PROPOSALS_V2)) && + event.valueJson.status === StatusEnum.Open, + }, + endpoint: async (event, env) => { + const daoAddress = await getDaoAddressForProposalModule({ + ...env, + contractAddress: event.contractAddress, + }) + if (!daoAddress) { + return + } + + return { + type: WebhookType.Url, + url: `https://telegram-notifier.dao-dao.workers.dev/${state.chainId}/${daoAddress}/notify`, + method: 'POST', + } + }, + getValue: async (event, getLastEvent, env) => { + // Only fire the webhook the first time this exists. + if ((await getLastEvent()) !== null) { + return + } + + // Get DAO config and proposal modules for this DAO so we can retrieve the + // DAO's name and the prefix for this proposal module. + const daoAddress = await getDaoAddressForProposalModule({ + ...env, + contractAddress: event.contractAddress, + }) + if (!daoAddress) { + return + } + + const daoConfig = await daoCoreConfig.compute({ + ...env, + contractAddress: daoAddress, + }) + const proposalModules = await activeProposalModules.compute({ + ...env, + contractAddress: daoAddress, + }) + const proposalModule = proposalModules?.find( + (proposalModule) => proposalModule.address === event.contractAddress + ) + + if (!daoConfig || !proposalModule) { + return + } + + // "proposals"|"proposals_v2", proposalNum + const [, proposalNum] = dbKeyToKeys(event.key, [false, true]) + const proposalId = `${proposalModule.prefix}${proposalNum}` + + return { + apiKey: config.telegramNotifierApiKey, + data: { + content: + `🎉 ${daoConfig.name} — Proposal ${proposalId} 🎉\n` + + config.daoDaoBase + + `/dao/${daoAddress}/proposals/${proposalId}`, + }, + } + }, +})