diff --git a/package.json b/package.json index 7c545ee..b975ae6 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "start": "NODE_ENV=development TZ=UTC yarn ts-node src/main.ts", "service:discord:message:update:consumer": "TZ=UTC NODE_ENV=development nodemon src/services/discord-message-consumer/main.ts", "service:discord:channel:cleaner:consumer": "TZ=UTC NODE_ENV=development nodemon src/services/discord-channel-cleaner-consumer/main.ts", - "apecoin:role:update": "yarn ts-node src/services/apecoin/index.ts" + "apecoin:role:update": "yarn ts-node src/services/apecoin/job.ts" }, "dependencies": { "@discordjs/builders": "^0.15.0", diff --git a/src/@types/discord-message-update.ts b/src/@types/discord-message-update.ts index d887ec4..c62c09e 100644 --- a/src/@types/discord-message-update.ts +++ b/src/@types/discord-message-update.ts @@ -4,6 +4,7 @@ export interface DiscordSQSMessage { users?: string[]; discordId?: string; publicAddress?: string; + daoName?: string; daos: { name: string; guildId: string; diff --git a/src/services/apecoin/index.ts b/src/services/apecoin/index.ts index f2ad36b..023699c 100644 --- a/src/services/apecoin/index.ts +++ b/src/services/apecoin/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-unsafe-return */ import axios from 'axios'; @@ -22,7 +23,7 @@ async function fetchDaoData() { } } -async function fetchDelegates(daoName: string) { +async function fetchDelegates(daoName: string, publicAddress?: string) { const delegateQuery = ` SELECT "t1"."discordHandle", "u"."publicAddress", @@ -36,9 +37,10 @@ async function fetchDelegates(daoName: string) { WHERE "t1"."daoName" = $1 AND "t2"."period" = '1y' AND "t1"."discordHandle" IS NOT NULL + ${publicAddress ? `AND "u"."publicAddress" = $2` : ''} `; try { - const result = await pool.query(delegateQuery, [daoName]); + const result = await pool.query(delegateQuery, [daoName, publicAddress]); return result?.rows; } catch (err) { console.error('Error fetching delegates:', err); @@ -58,19 +60,23 @@ async function fetchDelegateTrustLevel( } async function delegateHasPermission(delegate, dao) { - let hasPermission = + delegate.trustLevel = !delegate.handle ? 'no forum link' : 'already have the criterea'; + + delegate.hasPermission = delegate.offChainVotesPct >= 50 || (+delegate.balance >= 50000 && delegate.offChainVotesPct > 0); - if (!hasPermission && delegate.handle) { - const trustLevel = await fetchDelegateTrustLevel(delegate.handle, dao.forum); - hasPermission = hasPermission || trustLevel >= 2; + if (!delegate.hasPermission && delegate.handle) { + delegate.trustLevel = await fetchDelegateTrustLevel(delegate.handle, dao.forum); + delegate.hasPermission = delegate.hasPermission || delegate.trustLevel >= 2; } - return hasPermission; + return delegate; } -async function manageRoles(client: Client, guildId: string, handles: string[], action: string) { +async function manageRoles(client: Client, guildId: string, handles: any[], action: string) { + if (!handles.length) return; + const guild = client.guilds.cache.get(guildId); if (!guild) throw new Error('Guild not found'); @@ -80,24 +86,26 @@ async function manageRoles(client: Client, guildId: string, handles: string[], a if (!role) throw new Error('Role not found'); for (const handle of handles) { - const member = await guild.members.fetch(handle).catch(console.error); + const member = await guild.members.fetch(handle.discordHandle).catch(console.error); if (member) { await member.roles[action](role).catch(console.error); - console.log(`Role: ${role.name} | action: ${action} | user: ${member.user.tag}`); + console.log( + `Role: ${role.name} | action: ${action} | user: ${member.user.tag} | address: ${handle.publicAddress} | offChain: ${handle.offChainVotesPct} | balance: ${handle.balance} | forumLevel: ${handle.trustLevel} | hasCriterea: ${handle.hasPermission}` + ); } } } -(async () => { +export async function discordRoleManager(daoName?: string, publicAddress?: string) { const dao = await fetchDaoData(); - const delegates = await fetchDelegates(dao.name); + const delegates = await fetchDelegates(dao.name, publicAddress); - if (!delegates?.length) return; + if (!delegates?.length) return console.log('No delegates found'); const addHandles = []; - let revokeHandles = []; + const revokeHandles = []; const delegatesBalance = await getTokenBalance( delegates.map((d) => d.publicAddress), @@ -106,16 +114,11 @@ async function manageRoles(client: Client, guildId: string, handles: string[], a for (const delegate of delegates) { delegate.balance = +delegate.balance + (delegatesBalance?.[delegate.publicAddress] || 0); - const hasPermission = await delegateHasPermission(delegate, dao); + const delegateWithStatus = await delegateHasPermission(delegate, dao); - (hasPermission ? addHandles : revokeHandles).push(delegate.discordHandle); + (delegateWithStatus.hasPermission ? addHandles : revokeHandles).push(delegateWithStatus); } - revokeHandles = revokeHandles.filter((handle) => !addHandles.includes(handle)); - - console.log('addHandles', addHandles); - console.log('revokeHandles', revokeHandles); - const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.DIRECT_MESSAGES] }); @@ -131,4 +134,4 @@ async function manageRoles(client: Client, guildId: string, handles: string[], a }); client.login(process.env.DISCORD_TOKEN); -})(); +} diff --git a/src/services/apecoin/job.ts b/src/services/apecoin/job.ts new file mode 100644 index 0000000..c96dc5d --- /dev/null +++ b/src/services/apecoin/job.ts @@ -0,0 +1,3 @@ +import { discordRoleManager } from '.'; + +(async () => await discordRoleManager())(); diff --git a/src/services/discord-message-consumer/discord-message-consumer.service.ts b/src/services/discord-message-consumer/discord-message-consumer.service.ts index ba6b2b6..999b0ac 100644 --- a/src/services/discord-message-consumer/discord-message-consumer.service.ts +++ b/src/services/discord-message-consumer/discord-message-consumer.service.ts @@ -4,6 +4,7 @@ import { DiscordSQSMessage } from 'src/@types/discord-message-update'; import GetPastMessagesService from '../get-messages.service'; import { Client, Intents } from 'discord.js'; import { SentryService } from '../../sentry/sentry.service'; +import { discordRoleManager } from '../apecoin'; const LOG_CTX = 'DelegateStatUpdateConsumerService'; @@ -38,11 +39,16 @@ export class DiscordMessageConsumerService { parsedMessage = JSON.parse(message.message) as DiscordSQSMessage; console.log(`[${message.messageId}][${JSON.stringify(parsedMessage)}]`, LOG_CTX); - if (parsedMessage.daos) { - console.log(parsedMessage); - await this.getPastMessagesService.getMessages(client, parsedMessage); - } else { - console.log('no daos'); + console.log({ parsedMessage }); + + if (parsedMessage.reason === 'user-discord-link') { + if (parsedMessage.daos) { + await this.getPastMessagesService.getMessages(client, parsedMessage); + } else { + console.log('No Daos found in the message'); + } + } else if (parsedMessage.reason === 'delegate-update-discord-roles') { + await discordRoleManager(parsedMessage.daoName, parsedMessage.publicAddress); } console.log(`Time [${Date.now() - startTime}]`, LOG_CTX); diff --git a/src/services/get-messages.service.ts b/src/services/get-messages.service.ts index 9b3ad50..a4546b0 100644 --- a/src/services/get-messages.service.ts +++ b/src/services/get-messages.service.ts @@ -126,11 +126,21 @@ export default class GetPastMessagesService { async createUpdateDelegateStatsMessage( allMessagesToSave: MessageCustom[], publicAddress, + daoName, reason ) { - for (const message of _.uniqBy(allMessagesToSave, 'userId')) { + if (allMessagesToSave.length > 0) { + for (const message of _.uniqBy(allMessagesToSave, 'userId')) { + await this.delegateStatUpdateProducerService.produce({ + dao: message.daoName, + publicAddress, + reason, + timestamp: Date.now() + }); + } + } else { await this.delegateStatUpdateProducerService.produce({ - dao: message.daoName, + dao: daoName, publicAddress, reason, timestamp: Date.now() @@ -152,7 +162,6 @@ export default class GetPastMessagesService { try { const allBotGuilds = Array.from(await client.guilds.fetch()); const allUsers = [discordId || users].flat(); - const allMessagesToSave = []; let messagescount = 0; if (!daos.length) { @@ -163,6 +172,7 @@ export default class GetPastMessagesService { console.log('Servers -> bot is inside: ', allBotGuilds); for (const guild of daos) { + const allMessagesToSave = []; if (!allBotGuilds.find((item) => +item[0] === +guild.guildId)) continue; const formattedGuildChannels = guild.channelIds @@ -241,15 +251,19 @@ export default class GetPastMessagesService { console.log(err.message, channel); } } - } - - console.log('All messages count: ', messagescount); - console.log('allMessagesToSave length: ', allMessagesToSave.length); - if (allMessagesToSave.length > 0) { - await this.insertAllMessages(allMessagesToSave); + console.log('All messages count: ', messagescount); + console.log('allMessagesToSave length: ', allMessagesToSave.length); + if (allMessagesToSave.length > 0) { + await this.insertAllMessages(allMessagesToSave); + } if (reason === 'user-discord-link') { - await this.createUpdateDelegateStatsMessage(allMessagesToSave, publicAddress, reason); + await this.createUpdateDelegateStatsMessage( + allMessagesToSave, + publicAddress, + guild.name, + reason + ); } } } catch (err) {