Skip to content

Commit f8dd04f

Browse files
committed
feat: implement Discord profile data sync
1 parent 2fa8f0f commit f8dd04f

File tree

7 files changed

+100
-21
lines changed

7 files changed

+100
-21
lines changed

libs/api/bot/data-access/src/lib/api-bot-instances.service.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ export class ApiBotInstancesService {
7373
return this.core.data.bot.findMany({ where: { status: BotStatus.Active } })
7474
}
7575

76+
async getDefaultBot(): Promise<Bot | null> {
77+
return this.core.data.bot.findFirst({ where: { id: this.core.config.authDiscordClientId } })
78+
}
79+
80+
async getDefaultBotInstance(): Promise<DiscordBot> {
81+
const bot = await this.getDefaultBot()
82+
if (!bot) {
83+
throw new Error(`Can't get default bot`)
84+
}
85+
const instance = this.getBotInstance(bot.id)
86+
if (!instance) {
87+
throw new Error(`Can't get default bot instance`)
88+
}
89+
return instance
90+
}
91+
7692
async getBotRole({
7793
botId,
7894
roleId,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,45 @@
11
import { Injectable } from '@nestjs/common'
2+
import { IdentityProvider, Prisma } from '@prisma/client'
3+
import { ApiCoreService } from '@pubkey-link/api-core-data-access'
24
import { ApiBotDataAdminService } from './api-bot-data-admin.service'
35
import { ApiBotDataUserService } from './api-bot-data-user.service'
46
import { ApiBotInstancesService } from './api-bot-instances.service'
57

68
@Injectable()
79
export class ApiBotService {
810
constructor(
11+
readonly core: ApiCoreService,
912
readonly admin: ApiBotDataAdminService,
1013
readonly instances: ApiBotInstancesService,
1114
readonly user: ApiBotDataUserService,
1215
) {}
16+
17+
async syncDiscordIdentity({ providerId }: { providerId: string }) {
18+
const bot = await this.instances.getDefaultBotInstance()
19+
const user = await bot.getUser(providerId)
20+
21+
if (!user) {
22+
throw new Error(`Can't find discord user with id ${providerId}`)
23+
}
24+
25+
const identity = await this.core.data.identity.findFirst({
26+
where: {
27+
provider: IdentityProvider.Discord,
28+
providerId,
29+
},
30+
})
31+
32+
if (!identity) {
33+
throw new Error(`Can't find discord identity for id ${providerId}`)
34+
}
35+
await this.core.data.identity.update({
36+
where: { id: identity.id },
37+
data: {
38+
name: user.username,
39+
profile: user.toJSON() as Prisma.InputJsonValue,
40+
owner: { update: { avatarUrl: user?.avatarURL()?.toString() } },
41+
},
42+
})
43+
return true
44+
}
1345
}

libs/api/bot/util/src/lib/discord-bot.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { REST } from '@discordjs/rest'
22
import { Logger } from '@nestjs/common'
3-
import { Client, Guild, GuildMember, MessageCreateOptions, NonThreadGuildBasedChannel } from 'discord.js'
3+
import { Client, Guild, GuildMember, MessageCreateOptions, NonThreadGuildBasedChannel, User } from 'discord.js'
44
import { createDiscordClient, createDiscordRestClient } from './discord/client'
55

66
export interface RESTDiscordRoleConnection {
@@ -210,6 +210,10 @@ export class DiscordBot {
210210

211211
return (roles ?? []) as RESTDiscordRoleConnection[]
212212
}
213+
214+
async getUser(providerId: string): Promise<User | null> {
215+
return this.client?.users?.fetch(providerId, { force: true }) ?? null
216+
}
213217
}
214218

215219
export interface BotApplication {

libs/api/identity/data-access/src/lib/api-identity-data-admin.service.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { Injectable } from '@nestjs/common'
22
import { Identity as PrismaIdentity, IdentityProvider, UserStatus } from '@prisma/client'
3+
import { ApiBotService } from '@pubkey-link/api-bot-data-access'
34
import { ApiCoreService } from '@pubkey-link/api-core-data-access'
45
import { ApiNetworkAssetService } from '@pubkey-link/api-network-asset-data-access'
56
import { AdminCreateIdentityInput } from './dto/admin-create-identity.input'
67
import { AdminFindManyIdentityInput } from './dto/admin-find-many-identity.input'
78

89
@Injectable()
910
export class ApiIdentityDataAdminService {
10-
constructor(private readonly core: ApiCoreService, private readonly networkAsset: ApiNetworkAssetService) {}
11+
constructor(
12+
private readonly bot: ApiBotService,
13+
private readonly core: ApiCoreService,
14+
private readonly networkAsset: ApiNetworkAssetService,
15+
) {}
1116

1217
async createIdentity(input: AdminCreateIdentityInput): Promise<PrismaIdentity> {
1318
const found = await this.core.data.identity.findUnique({
@@ -107,15 +112,26 @@ export class ApiIdentityDataAdminService {
107112
if (!identity) {
108113
throw new Error(`Identity ${identityId} not found`)
109114
}
110-
if (identity.provider !== IdentityProvider.Solana) {
111-
throw new Error(`Identity ${identityId} not supported`)
115+
116+
switch (identity.provider) {
117+
case IdentityProvider.Discord:
118+
return this.bot
119+
.syncDiscordIdentity({ providerId: identity.providerId })
120+
.then((res) => !!res)
121+
.catch((err) => {
122+
console.log('Error syncing identity', err)
123+
return false
124+
})
125+
case IdentityProvider.Solana:
126+
return this.networkAsset.sync
127+
.syncIdentity({ owner: identity.providerId })
128+
.then((res) => !!res)
129+
.catch((err) => {
130+
console.log('Error syncing identity', err)
131+
return false
132+
})
133+
default:
134+
throw new Error(`Identity provider ${identity.provider} not supported`)
112135
}
113-
return this.networkAsset.sync
114-
.syncIdentity({ owner: identity.providerId })
115-
.then((res) => !!res)
116-
.catch((err) => {
117-
console.log('Error syncing identity', err)
118-
return false
119-
})
120136
}
121137
}

libs/api/identity/data-access/src/lib/api-identity.data-access.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Module } from '@nestjs/common'
22
import { ApiAuthDataAccessModule } from '@pubkey-link/api-auth-data-access'
3+
import { ApiBotDataAccessModule } from '@pubkey-link/api-bot-data-access'
34
import { ApiCoreDataAccessModule } from '@pubkey-link/api-core-data-access'
45
import { ApiNetworkAssetDataAccessModule } from '@pubkey-link/api-network-asset-data-access'
56
import { ApiNetworkDataAccessModule } from '@pubkey-link/api-network-data-access'
@@ -12,6 +13,7 @@ import { ApiIdentityService } from './api-identity.service'
1213
@Module({
1314
imports: [
1415
ApiAuthDataAccessModule,
16+
ApiBotDataAccessModule,
1517
ApiNetworkDataAccessModule,
1618
ApiCoreDataAccessModule,
1719
ApiNetworkAssetDataAccessModule,

libs/api/identity/feature/src/lib/api-identity.resolver.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import { getIdentityUrl, Identity } from '@pubkey-link/api-identity-data-access'
66
export class ApiIdentityResolver {
77
@ResolveField(() => String, { nullable: true })
88
avatarUrl(@Parent() identity: Identity) {
9-
return (identity.profile as { avatarUrl?: string })?.avatarUrl ?? null
9+
return (
10+
(identity.profile as { avatarUrl?: string })?.avatarUrl ??
11+
(identity.profile as { avatarURL?: string })?.avatarURL ??
12+
null
13+
)
1014
}
1115

1216
@ResolveField(() => Boolean, { nullable: true })

libs/web/identity/data-access/src/lib/use-admin-find-many-identity.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,20 @@ export function useAdminFindManyIdentity({ ownerId, provider }: { ownerId?: stri
6565
})
6666
},
6767
syncIdentity: async (identity: Identity) => {
68-
return sdk.adminSyncIdentity({ identityId: identity.id }).then(async (res) => {
69-
if (res) {
70-
toastSuccess('Identity synced')
71-
await query.refetch()
72-
return true
73-
}
74-
toastError('Error syncing identity')
75-
return false
76-
})
68+
return sdk
69+
.adminSyncIdentity({ identityId: identity.id })
70+
.then(async (res) => {
71+
if (res) {
72+
toastSuccess('Identity synced')
73+
await query.refetch()
74+
return true
75+
}
76+
toastError('Error syncing identity')
77+
return false
78+
})
79+
.catch((err) => {
80+
toastError(err)
81+
})
7782
},
7883
}
7984
}

0 commit comments

Comments
 (0)