Skip to content

Commit

Permalink
feat: set up syncing of bot members
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Feb 10, 2024
1 parent 782202f commit 7a384f9
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 23 deletions.
59 changes: 49 additions & 10 deletions libs/api/bot/data-access/src/lib/api-bot-manager.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
import { Cron, CronExpression } from '@nestjs/schedule'
import { Bot } from '@prisma/client'

import { createDiscordRestClient, DiscordBot } from '@pubkey-link/api-bot-util'
Expand Down Expand Up @@ -170,11 +171,6 @@ export class ApiBotManagerService implements OnModuleInit {
}

async syncBotServer(userId: string, botId: string, serverId: string) {
const bot = this.ensureBotInstance(botId)
if (!bot) {
console.log(`Can't find bot.`, botId, serverId)
return false
}
const community = await this.core.data.community.findFirst({
where: { bot: { id: botId } },
include: { bot: true },
Expand All @@ -183,13 +179,48 @@ export class ApiBotManagerService implements OnModuleInit {
console.log(`Can't find community.`, botId, serverId)
return false
}
await this.core.ensureCommunityAdmin({ userId, communityId: community.id })
await this.syncBotServerMembers({
communityId: community.id,
botId,
serverId,
})
return true
}

@Cron(CronExpression.EVERY_5_MINUTES)
private async syncBotServers() {
const bots = await this.core.data.bot.findMany({ where: { status: BotStatus.Active } })

for (const bot of bots) {
const servers = await this.getBotServers('-no-user-id-', bot.id)
for (const server of servers) {
await this.syncBotServerMembers({ botId: bot.id, communityId: bot.communityId, serverId: server.id })
}
}
}

async syncBotServerMembers({
botId,
communityId,
serverId,
}: {
botId: string
communityId: string
serverId: string
}) {
const discordBot = this.ensureBotInstance(botId)
if (!discordBot) {
console.log(`Can't find bot.`, botId, serverId)
return false
}
this.logger.verbose(`Fetching members... ${botId} ${serverId}`)

const [discordIdentityIds, botMemberIds] = await Promise.all([
this.botMember.getDiscordIdentityIds(),
this.botMember.getBotMemberIds(botId, serverId),
])
const members = await bot.getDiscordServerMembers(serverId)
const members = await discordBot.getDiscordServerMembers(serverId)

this.logger.verbose(`Found ${members.length} members to process`)
const filtered = members
Expand All @@ -202,20 +233,28 @@ export class ApiBotManagerService implements OnModuleInit {

if (toBeDeleted.length) {
this.logger.warn(`Found ${toBeDeleted.length} members to delete`)
this.logger.warn(`TODO: DELETE MEMBERS`)
for (const userId of toBeDeleted) {
this.logger.verbose(`Removing member ${userId} from bot ${botId} server ${serverId}...`)
await this.botMember.scheduleRemoveMember({ communityId, botId, serverId, userId })
}
}

this.logger.verbose(`Found ${filtered.length} members to process (filtered)`)
let linkedCount = 0
for (const member of filtered) {
const userId = member.id
await this.botMember.scheduleAddMember(community.bot, serverId, userId)

this.logger.verbose(`${botId} ${serverId} Processed ${member.user.username} (linked: ${!!member.id})`)
await this.botMember.scheduleAddMember({ communityId, botId: botId, serverId, userId })
if (member.id) {
linkedCount++
}
}
await this.core.logInfo(
`Found ${members.length} members to process (filtered ${filtered.length}) (linked ${linkedCount})`,
{
botId,
communityId,
},
)
this.logger.verbose(
`Found ${members.length} members to process (filtered ${filtered.length}) (linked ${linkedCount})`,
)
Expand Down
50 changes: 42 additions & 8 deletions libs/api/bot/data-access/src/lib/api-bot-member.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,22 @@ export class ApiBotMemberService {
return
}
this.logger.verbose(`Setting up listeners for bot ${bot.name}`)
instance.client?.on('guildMemberAdd', (member) => this.scheduleAddMember(bot, member.guild.id, member.id))
instance.client.on('guildMemberRemove', (member) => this.scheduleRemoveMember(bot, member.guild.id, member.id))
instance.client?.on('guildMemberAdd', (member) =>
this.scheduleAddMember({
botId: bot.id,
communityId: bot.communityId,
serverId: member.guild.id,
userId: member.id,
}),
)
instance.client.on('guildMemberRemove', (member) =>
this.scheduleRemoveMember({
botId: bot.id,
communityId: bot.communityId,
serverId: member.guild.id,
userId: member.id,
}),
)
}

async upsert({
Expand Down Expand Up @@ -146,21 +160,41 @@ export class ApiBotMemberService {
})
}

async scheduleAddMember(bot: Bot, serverId: string, userId: string) {
const jobId = `${bot.id}-${serverId}-${userId}`
async scheduleAddMember({
botId,
communityId,
serverId,
userId,
}: {
botId: string
communityId: string
serverId: string
userId: string
}) {
const jobId = `${botId}-${serverId}-${userId}`
await this.botMemberAddQueue
.add('member-add', { botId: bot.id, communityId: bot.communityId, serverId, userId })
.add('member-add', { botId, communityId, serverId, userId })
.then((res) => {
this.logger.verbose(`scheduleAddMember queued: ${res.id}`)
})
.catch((err) => {
this.logger.error(`scheduleAddMember error: ${jobId}: ${err}`)
})
}
async scheduleRemoveMember(bot: Bot, serverId: string, userId: string) {
const jobId = `${bot.id}-${serverId}-${userId}`
async scheduleRemoveMember({
botId,
communityId,
serverId,
userId,
}: {
botId: string
communityId: string
serverId: string
userId: string
}) {
const jobId = `${botId}-${serverId}-${userId}`
await this.botMemberRemoveQueue
.add('member-remove', { botId: bot.id, communityId: bot.communityId, serverId, userId })
.add('member-remove', { botId, communityId, serverId, userId })
.then((res) => {
this.logger.verbose(`scheduleRemoveMember queued: ${res.id}`)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class ApiNetworkAssetSyncService {
return !!job.id
}

@Cron(CronExpression.EVERY_MINUTE)
@Cron(CronExpression.EVERY_10_MINUTES)
async syncAll() {
const identities = await this.core.data.identity.findMany({
where: {
Expand Down
21 changes: 17 additions & 4 deletions libs/web/user/feature/src/lib/user-user-detail-feature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ import { UserNetworkTokenFeature } from '@pubkey-link/web-network-token-feature'
import { UiGrid } from '@pubkey-link/web-ui-core'
import { useUserFineOneUser } from '@pubkey-link/web-user-data-access'
import { UserUiProfile } from '@pubkey-link/web-user-ui'
import { UiCard, UiContainer, UiDebugModal, UiGroup, UiLoader, UiStack, UiTabRoutes, UiWarning } from '@pubkey-ui/core'
import {
UiCard,
UiContainer,
UiDebugModal,
UiGroup,
UiLoader,
UiStack,
UiTabRoutes,
UiTime,
UiWarning,
} from '@pubkey-ui/core'
import { Link, useParams } from 'react-router-dom'

export function UserUserDetailFeature() {
Expand Down Expand Up @@ -43,13 +53,16 @@ export function UserUserDetailFeature() {
/>
{items?.map((identity) => (
<UiCard key={identity.id} p="xs">
<UiGroup align="start">
<Group>
<IdentityUiIcon provider={identity.provider} />
<UiGroup align="center">
<Group gap="xs">
<IdentityUiIcon size={28} provider={identity.provider} />
<Stack gap={0}>
<Text size="sm" fw="bold">
{ellipsify(identity.name ?? identity.providerId, 6)}
</Text>
{identity.syncEnded ? (
<UiTime size="xs" c="dimmed" prefix="Synced " date={new Date(identity.syncEnded)} />
) : null}
</Stack>
</Group>
<Group gap={2}>
Expand Down

0 comments on commit 7a384f9

Please sign in to comment.