Skip to content

Commit

Permalink
✨ [Discord] Broader support for urls (#46)
Browse files Browse the repository at this point in the history
* ⬆️ [Dependency] Small dependency bumps

* ✨ [Discord] Broaden support for URL variations

* 📝 [Changeset] Create report

* ♻️ [Discord] Changes to better support consumers
  • Loading branch information
beefchimi authored Mar 1, 2022
1 parent 56fab2a commit ca2d059
Show file tree
Hide file tree
Showing 16 changed files with 604 additions and 212 deletions.
5 changes: 5 additions & 0 deletions .changeset/lazy-doors-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'socialitejs': patch
---

Support more variations for Discord urls.
344 changes: 172 additions & 172 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@
"@beefchimi/typescript-config": "^0.0.9",
"@changesets/changelog-github": "^0.4.3",
"@changesets/cli": "^2.21.0",
"@types/node": "^17.0.19",
"@vitest/ui": "^0.5.3",
"@types/node": "^17.0.21",
"@vitest/ui": "^0.5.9",
"c8": "^7.11.0",
"vite": "^2.8.4",
"vite": "^2.8.5",
"vite-plugin-dts": "^0.9.9",
"vitest": "^0.5.3"
"vitest": "^0.5.9"
}
}
10 changes: 10 additions & 0 deletions src/capture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ export const defaultUserMatcher = {
subdomain: /[^.]+/,
path: /[^/]+/,
};

// TODO: This should probably be re-located elsewhere
// https://github.com/beefchimi/socialite/issues/35
export const discordPreferredUrls = {
users: `https://discordapp.com/users/${profileReplacement.user}`,
channels: `https://discord.com/channels/${profileReplacement.user}`,
vanity: `https://discord.gg/${profileReplacement.user}`,
// TODO: This result should not be supported
default: `https://discord.com/${profileReplacement.user}`,
};
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ export {
filterNullishValuesFromObject,
filterNetworkProperties,
mergeRegExp,
getDiscordPreferredUrl,
getUrlGroups,
getUrlWithSubstitutions,
} from './utilities';

export {MatchUserSource, UrlCaptureId} from './types';
export type {
DiscordUrlCriteria,
UrlAnatomy,
SocialiteNetwork,
SocialiteProfile,
Expand Down
21 changes: 18 additions & 3 deletions src/networks/discord.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import {profileReplacement} from '../capture';
import {discordPreferredUrls} from '../capture';
import type {SocialiteNetwork} from '../types';

// Discord is difficult to solve given Socialite's current design.
// There are essentially 3 different urls to support:
// 1. User profiles (discordapp.com/users/*)
// 2. Server/channel urls (discord.com/channels/{serverid}/{channelid})
// 3. Official vanity urls (discord.gg/*)
// Since we are not yet validating against a Top-level domain (.gg),
// any `discord` url validates as true and captures the `path`.
// This degrades the confidence provided by `users` or `channels`.

// TODO: Solve this problem by improving `preferredUrl` and parsing criteria.
// https://github.com/beefchimi/socialite/issues/35
export const discord: SocialiteNetwork = {
id: 'discord',
preferredUrl: `https://discordapp.com/users/${profileReplacement.user}`,
preferredUrl: discordPreferredUrls.users,
matcher: {
domain: /discord/,
user: /^(?:\/users\/)([^/]+)/,
user: /^\/(?:users\/|channels\/)?([^/]+)/,
// TODO: If we want to support capturing EVERYTHING after
// the first `/` (necessary for capturing the `channelid`),
// then we would need to use the following:
// user: /^\/(?:users\/|channels\/)?(.+)/,
},
};
120 changes: 98 additions & 22 deletions src/networks/tests/discord.test.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,114 @@
import {Socialite} from '../../socialite';
import type {SocialiteProfile} from '../../types';
import {allSocialiteNetworks, mockGenericUser} from '../../tests/fixtures';
import {
allSocialiteNetworks,
discordValidUrls,
mockGenericUser,
} from '../../tests/fixtures';
import {discord} from '../discord';

describe('Social networks > discord', () => {
const mockSocialite = new Socialite(allSocialiteNetworks);
const mockCommonUrl = `https://www.discordapp.com/users/${mockGenericUser}`;

it('returns expected `id` and `user` from common url', () => {
const {id, user} = mockSocialite.parseProfile(
mockCommonUrl,
) as SocialiteProfile;
// TODO: We want to resolve these in the future
// https://github.com/beefchimi/socialite/issues/35
describe('bogus', () => {
it('mistakenly returns the first path match for any `discord` url', () => {
const mockPageSlug = 'about-us-page';
const mockBogusUrl = `https://www.discord.com/${mockPageSlug}`;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
const {id, user} = mockSocialite.parseProfile(
mockBogusUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockPageSlug);
});
});

describe('users', () => {
const mockUsersUrl = `https://www.discordapp.com/users/${mockGenericUser}`;

it('returns `id` and `user`', () => {
const {id, user} = mockSocialite.parseProfile(
mockUsersUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
});

it('omits any trailing path after the first `user` match', () => {
const mockUsersTrailingUrl = `${mockUsersUrl}/trail-123`;

const {id, user} = mockSocialite.parseProfile(
mockUsersTrailingUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
});
});

it('returns expected `id` and `user` from url with trailing path', () => {
const mockUncommonUrl = `${mockCommonUrl}/trail-123`;
const {id, user} = mockSocialite.parseProfile(
mockUncommonUrl,
) as SocialiteProfile;
describe('channels', () => {
const mockChannelsUrl = `https://www.discord.com/channels/${mockGenericUser}`;

it('returns `id` and `user`', () => {
const {id, user} = mockSocialite.parseProfile(
mockChannelsUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
});

it('omits any trailing path after the first `user` match', () => {
const mockChannelsTrailingUrl = `${mockChannelsUrl}/trail-123`;

const {id, user} = mockSocialite.parseProfile(
mockChannelsTrailingUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
});
});

describe('vanity', () => {
const mockVanityUrl = `https://discord.gg/${mockGenericUser}`;

it('returns `id` and `user`', () => {
const {id, user} = mockSocialite.parseProfile(
mockVanityUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
});

it('omits any trailing path after the first `user` match', () => {
const mockVanityTrailingUrl = `${mockVanityUrl}/trail-123`;

const {id, user} = mockSocialite.parseProfile(
mockVanityTrailingUrl,
) as SocialiteProfile;

expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
expect(id).toBe(discord.id);
expect(user).toBe(mockGenericUser);
});
});

it('returns `id` with no `user` when provided an unrecognized leading path', () => {
const mockUnsupportedUrl = `https://discordapp.com/foo/${mockGenericUser}`;
const match = mockSocialite.parseProfile(
mockUnsupportedUrl,
) as SocialiteProfile;
describe('all variations', () => {
it('returns `id` and `user`', () => {
discordValidUrls.forEach(({originalUrl, preferredUrl, user}) => {
const match = mockSocialite.parseProfile(
originalUrl,
) as SocialiteProfile;

expect(match.id).toBe(discord.id);
expect(match.user).toBeUndefined();
expect(match.id).toBe(discord.id);
expect(match.preferredUrl).toBe(preferredUrl);
expect(match.user).toBe(user);
});
});
});
});
17 changes: 12 additions & 5 deletions src/socialite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
} from './types';
import {
filterNetworkProperties,
getDiscordPreferredUrl,
getUrlGroups,
getUrlWithSubstitutions,
} from './utilities';
Expand Down Expand Up @@ -155,11 +156,17 @@ export class Socialite {
return minResult;
}

const preferredUrl = getUrlWithSubstitutions(
targetNetwork.preferredUrl,
user,
prefix,
);
// TODO: Resolve this special condition
// https://github.com/beefchimi/socialite/issues/35
const preferredUrl =
targetNetwork.id === 'discord'
? getDiscordPreferredUrl({
tldomain: minResult.urlGroups.tldomain,
path: minResult.urlGroups.path,
user,
})
: getUrlWithSubstitutions(targetNetwork.preferredUrl, user, prefix);

const appUrl = targetNetwork.appUrl
? getUrlWithSubstitutions(targetNetwork.appUrl, user, prefix)
: undefined;
Expand Down
Loading

0 comments on commit ca2d059

Please sign in to comment.