Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions community/src/api_v0/message/reactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
LinkedReactionUser,
ReactionUsersByEmoji,
} from './reactions';
import type { DiscordMessage } from '../../lib/community/type';
import type { CommunityMessage } from '../../lib/community/type';

const eventMessage: EventMessageRecord = {
id: 'event-1',
Expand All @@ -19,15 +19,15 @@ const eventMessage: EventMessageRecord = {
updatedAt: '2026-07-01T00:00:00Z',
};

const discordMessage: DiscordMessage = {
const discordMessage: CommunityMessage = {
id: eventMessage.messageId,
channelId: eventMessage.channelId,
content: eventMessage.content,
createdAt: eventMessage.createdAt,
author: {
id: '323456789012345678',
username: 'bot',
globalName: null,
displayName: null,
bot: true,
},
reactions: [
Expand All @@ -41,15 +41,15 @@ const reactionUsersByEmoji: ReactionUsersByEmoji[] = [
emoji: '✅',
count: 2,
users: [
{ id: '423456789012345678', username: 'taro', globalName: 'Taro', bot: false },
{ id: '523456789012345678', username: 'hanako', globalName: null, bot: false },
{ id: '423456789012345678', username: 'taro', displayName: 'Taro', bot: false },
{ id: '523456789012345678', username: 'hanako', displayName: null, bot: false },
],
},
{
emoji: '🍱',
count: 1,
users: [
{ id: '423456789012345678', username: 'taro', globalName: 'Taro', bot: false },
{ id: '423456789012345678', username: 'taro', displayName: 'Taro', bot: false },
],
},
];
Expand Down
8 changes: 4 additions & 4 deletions community/src/api_v0/message/reactions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DiscordMessage, DiscordReactionUser } from "../../lib/community/type";
import type { CommunityMessage, CommunityReactionUser } from "../../lib/community/type";
import type { MemberStatus } from "../../../../share/drizzle/schema";

export type EventMessageRecord = {
Expand Down Expand Up @@ -38,7 +38,7 @@ export type LinkedReactionUser = {
export type ReactionUsersByEmoji = {
emoji: string;
count: number;
users: DiscordReactionUser[];
users: CommunityReactionUser[];
};

export type ReactionMember = {
Expand Down Expand Up @@ -76,7 +76,7 @@ export const collectDiscordUserIds = (reactionUsersByEmoji: ReactionUsersByEmoji

export const buildMessageReactionSummary = (
eventMessage: EventMessageRecord,
discordMessage: DiscordMessage,
discordMessage: CommunityMessage,
reactionUsersByEmoji: ReactionUsersByEmoji[],
linkedUsers: LinkedReactionUser[],
) => {
Expand All @@ -91,7 +91,7 @@ export const buildMessageReactionSummary = (
const reactionMember: ReactionMember = {
discordUserId: discordUser.id,
discordUsername: discordUser.username,
discordGlobalName: discordUser.globalName,
discordGlobalName: discordUser.displayName,
userId: linkedUser?.userId ?? null,
userName: linkedUser?.userName ?? null,
displayName: linkedUser?.displayName ?? null,
Expand Down
4 changes: 2 additions & 2 deletions community/src/api_v0/user/me/identity/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const linkedAccount = {
const oauthUser = {
id: linkedAccount.accountId,
username: 'test-user',
globalName: 'Test User',
displayName: 'Test User',
avatarUrl: null,
};
const guildMembership = {
Expand All @@ -30,7 +30,7 @@ const persisted = {
provider: 'discord' as const,
providerAccountId: linkedAccount.accountId,
username: oauthUser.username,
providerDisplayName: oauthUser.globalName,
providerDisplayName: oauthUser.displayName,
avatarUrl: null,
oauthVerifiedAt: fixedNow,
membership: {
Expand Down
16 changes: 8 additions & 8 deletions community/src/api_v0/user/me/identity/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { getAuth } from '../../../../auth/better-auth';
import { CommunityProviderError } from '../../../../lib/community/error';
import { getCurrentDiscordUser } from '../../../../lib/community/discord/oauth';
import type {
DiscordGuildMembership,
DiscordOAuthUser,
CommunityAccountProfile,
CommunityMembership,
} from '../../../../lib/community/type';
import {
verifyDiscordIdentityRoute,
Expand All @@ -27,9 +27,9 @@ type LinkedDiscordAccount = {
type PersistVerificationInput = {
appUserId: string;
linkedAccount: LinkedDiscordAccount;
oauthUser: DiscordOAuthUser;
oauthUser: CommunityAccountProfile;
guildId: string;
guildMembership: DiscordGuildMembership | null;
guildMembership: CommunityMembership | null;
verifiedAt: string;
};

Expand All @@ -43,7 +43,7 @@ type IdentityAuthApi = {

type VerificationDependencies = {
getAuthApi(c: Context<AppContext>): IdentityAuthApi;
getCurrentDiscordUser(accessToken: string): Promise<DiscordOAuthUser>;
getCurrentDiscordUser(accessToken: string): Promise<CommunityAccountProfile>;
findLinkedDiscordAccounts(
c: Context<AppContext>,
userId: string,
Expand Down Expand Up @@ -80,7 +80,7 @@ const persistVerification: VerificationDependencies['persistVerification'] = asy
provider: 'discord',
providerAccountId: input.oauthUser.id,
username: input.oauthUser.username,
providerDisplayName: input.oauthUser.globalName,
providerDisplayName: input.oauthUser.displayName,
avatarUrl: input.oauthUser.avatarUrl,
oauthVerifiedAt: input.verifiedAt,
lastSyncedAt: input.verifiedAt,
Expand All @@ -92,7 +92,7 @@ const persistVerification: VerificationDependencies['persistVerification'] = asy
authAccountId: input.linkedAccount.id,
providerAccountId: input.oauthUser.id,
username: input.oauthUser.username,
providerDisplayName: input.oauthUser.globalName,
providerDisplayName: input.oauthUser.displayName,
avatarUrl: input.oauthUser.avatarUrl,
oauthVerifiedAt: input.verifiedAt,
lastSyncedAt: input.verifiedAt,
Expand Down Expand Up @@ -222,7 +222,7 @@ export const createVerifyDiscordIdentityService = (

const [linkedAccount] = linkedAccounts;
let accessToken: string;
let oauthUser: DiscordOAuthUser;
let oauthUser: CommunityAccountProfile;

try {
({ accessToken } = await authApi.getAccessToken({
Expand Down
10 changes: 5 additions & 5 deletions community/src/lib/community/discord/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CommunityProvider } from '../interface';
import type { DiscordGuildMembership, DiscordMessage, DiscordReactionUser, Role, SendMessageInput, SendMessageResult } from '../type';
import type { CommunityMembership, CommunityMessage, CommunityReactionUser, CommunityRole, SendMessageInput, SendMessageResult } from '../type';
import { CommunityProviderError } from '../error';
import { getGuildMembershipAPI, listUserRolesAPI } from './role';
import { getMessageAPI, listMessageReactionUsersAPI, sendMessageAPI } from './message';
Expand Down Expand Up @@ -31,23 +31,23 @@ export class DiscordProvider implements CommunityProvider {
return response.json();
}

async listUserRoles(userId: string): Promise<Role[]> {
async listUserRoles(userId: string): Promise<CommunityRole[]> {
return listUserRolesAPI(this, userId);
}

async getGuildMembership(userId: string): Promise<DiscordGuildMembership> {
async getGuildMembership(userId: string): Promise<CommunityMembership> {
return getGuildMembershipAPI(this, userId);
}

async sendMessage(input: SendMessageInput): Promise<SendMessageResult> {
return sendMessageAPI(this, input);
}

async getMessage(channelId: string, messageId: string): Promise<DiscordMessage> {
async getMessage(channelId: string, messageId: string): Promise<CommunityMessage> {
return getMessageAPI(this, channelId, messageId);
}

async listMessageReactionUsers(channelId: string, messageId: string, emoji: string): Promise<DiscordReactionUser[]> {
async listMessageReactionUsers(channelId: string, messageId: string, emoji: string): Promise<CommunityReactionUser[]> {
return listMessageReactionUsersAPI(this, channelId, messageId, emoji);
}
}
2 changes: 1 addition & 1 deletion community/src/lib/community/discord/message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ test('listMessageReactionUsersAPI fetches and parses users who reacted to a mess
assert.deepEqual(users, [{
id: '623456789012345678',
username: 'taro',
globalName: 'Taro',
displayName: 'Taro',
bot: false,
}]);
});
Expand Down
31 changes: 17 additions & 14 deletions community/src/lib/community/discord/message.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import type { DiscordProvider } from './main';
import { CommunityProviderError } from '../error';
import type {
CommunityMessage,
CommunityReactionUser,
SendMessageInput,
SendMessageResult
} from '../type';
import { SendMessageResultSchema } from '../type';
import {
DiscordMessage,
DISCORD_MESSAGE_CONTENT_LIMIT,
DiscordMessageSchema,
DiscordReactionUser,
DiscordReactionUserSchema,
SendMessageInput,
SendMessageInputSchema,
SendMessageResult,
SendMessageResultSchema
} from '../type';
DiscordSendMessageInputSchema
} from './schema';

// チャンネルへメッセージを送信し、送信したメッセージIDを返す
export async function sendMessageAPI(
provider: DiscordProvider,
input: SendMessageInput
): Promise<SendMessageResult> {
const parsedInput = SendMessageInputSchema.parse(input);
const parsedInput = DiscordSendMessageInputSchema.parse(input);
const roleIds = parsedInput.mentionRoleIds ?? [];

// メンション対象のロールをメッセージ本文に埋め込む(<@&ロールID>)
const mentions = roleIds.map((id) => `<@&${id}>`).join(' ');
const content = mentions ? `${mentions} ${parsedInput.content}` : parsedInput.content;

if (content.length > 2000) {
if (content.length > DISCORD_MESSAGE_CONTENT_LIMIT) {
throw new CommunityProviderError('Discord message content is too long', 400, 'discord');
}

Expand All @@ -46,7 +49,7 @@ export async function getMessageAPI(
provider: DiscordProvider,
channelId: string,
messageId: string
): Promise<DiscordMessage> {
): Promise<CommunityMessage> {
const message = await provider.request<any>(
'GET',
`/channels/${channelId}/messages/${messageId}`
Expand All @@ -60,7 +63,7 @@ export async function getMessageAPI(
author: {
id: message.author.id,
username: message.author.username,
globalName: message.author.global_name ?? null,
displayName: message.author.global_name ?? null,
bot: message.author.bot ?? false,
},
reactions: (message.reactions ?? []).map((reaction: any) => ({
Expand All @@ -75,8 +78,8 @@ export async function listMessageReactionUsersAPI(
channelId: string,
messageId: string,
emoji: string
): Promise<DiscordReactionUser[]> {
const users: DiscordReactionUser[] = [];
): Promise<CommunityReactionUser[]> {
const users: CommunityReactionUser[] = [];
let after: string | undefined;

while (true) {
Expand All @@ -92,7 +95,7 @@ export async function listMessageReactionUsersAPI(
DiscordReactionUserSchema.parse({
id: user.id,
username: user.username,
globalName: user.global_name ?? null,
displayName: user.global_name ?? null,
bot: user.bot ?? false,
})
);
Expand Down
2 changes: 1 addition & 1 deletion community/src/lib/community/discord/oauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ test('getCurrentDiscordUser verifies the bearer token and maps the Discord profi
assert.deepEqual(user, {
id: '123456789012345678',
username: 'club-member',
globalName: 'Club Member',
displayName: 'Club Member',
avatarUrl: 'https://cdn.discordapp.com/avatars/123456789012345678/avatar-hash.png',
});
});
Expand Down
9 changes: 5 additions & 4 deletions community/src/lib/community/discord/oauth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CommunityProviderError } from '../error';
import { DiscordOAuthUser, DiscordOAuthUserSchema } from '../type';
import type { CommunityAccountProfile } from '../type';
import { DiscordAccountProfileSchema } from './schema';

const DISCORD_API_BASE = 'https://discord.com/api/v10';

Expand All @@ -22,7 +23,7 @@ const buildDiscordAvatarUrl = (profile: DiscordOAuthProfile): string | null => {
export const getCurrentDiscordUser = async (
accessToken: string,
fetcher: typeof fetch = fetch,
): Promise<DiscordOAuthUser> => {
): Promise<CommunityAccountProfile> => {
const response = await fetcher(`${DISCORD_API_BASE}/users/@me`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Expand All @@ -41,10 +42,10 @@ export const getCurrentDiscordUser = async (
}

const profile = await response.json<DiscordOAuthProfile>();
const parsed = DiscordOAuthUserSchema.safeParse({
const parsed = DiscordAccountProfileSchema.safeParse({
id: profile.id,
username: profile.username,
globalName: profile.global_name ?? null,
displayName: profile.global_name ?? null,
avatarUrl: buildDiscordAvatarUrl(profile),
});

Expand Down
17 changes: 8 additions & 9 deletions community/src/lib/community/discord/role.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { DiscordProvider } from './main';
import type { CommunityMembership, CommunityRole } from '../type';
import {
DiscordGuildMembership,
DiscordGuildMembershipSchema,
DiscordMembershipSchema,
DiscordRoleSchema,
DiscordSnowflakeSchema,
Role,
RoleSchema,
} from '../type';
} from './schema';
import { CommunityProviderError } from '../error';

type DiscordGuildMemberResponse = {
Expand All @@ -22,7 +21,7 @@ type DiscordRoleResponse = {
export async function getGuildMembershipAPI(
provider: DiscordProvider,
userId: string,
): Promise<DiscordGuildMembership> {
): Promise<CommunityMembership> {
const expectedUserId = DiscordSnowflakeSchema.parse(userId);
const member = await provider.request<DiscordGuildMemberResponse>(
'GET',
Expand All @@ -45,12 +44,12 @@ export async function getGuildMembershipAPI(
);
const roleMap = new Map(
allRoles.map((role) => {
const parsed = RoleSchema.parse(role);
const parsed = DiscordRoleSchema.parse(role);
return [parsed.id, parsed] as const;
}),
);

return DiscordGuildMembershipSchema.parse({
return DiscordMembershipSchema.parse({
userId: expectedUserId,
nickname: member.nick ?? null,
roles: memberRoleIds.flatMap((roleId) => {
Expand All @@ -60,6 +59,6 @@ export async function getGuildMembershipAPI(
});
}

export async function listUserRolesAPI(provider: DiscordProvider, userId: string): Promise<Role[]> {
export async function listUserRolesAPI(provider: DiscordProvider, userId: string): Promise<CommunityRole[]> {
return (await getGuildMembershipAPI(provider, userId)).roles;
}
Loading
Loading