util.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /* eslint-disable camelcase */
  2. import * as querystring from "querystring";
  3. import { Option, tryDo, isHttpError } from "@shared/common/async_utils";
  4. import got, { HTTPError } from "got";
  5. import { logger } from "./logging";
  6. const DISCORD_API = "https://discord.com/api";
  7. export interface AccessTokenResponse {
  8. access_token: string;
  9. token_type: string;
  10. expires_in: number;
  11. refresh_token: string;
  12. scope: string;
  13. }
  14. type GrantType = "authorization_code";
  15. type TokenType = "code" | "token";
  16. export interface AccessTokenRequest {
  17. client_id: string;
  18. client_secret: string;
  19. grant_type: GrantType;
  20. code: string;
  21. scope: string;
  22. redirect_uri: string;
  23. }
  24. export type AuthorizeRequest = {
  25. client_id: string,
  26. redirect_url: string,
  27. response_type: TokenType,
  28. scope: string,
  29. }
  30. export interface DiscordUser {
  31. id: string;
  32. username: string;
  33. discriminator: string;
  34. avatar?: string;
  35. bot?: boolean;
  36. mfa_enabled?: boolean;
  37. locale?: string;
  38. verified?: boolean;
  39. email?: string;
  40. flags?: number;
  41. premium_type?: number;
  42. }
  43. export class DiscordAPI {
  44. static getAuthUrl(opts: AuthorizeRequest): string {
  45. return `${DISCORD_API}/oauth2/authorize?${querystring.stringify(opts)}`;
  46. }
  47. static async getToken(opts: AccessTokenRequest):
  48. Promise<Option<AccessTokenResponse, { error: string }>> {
  49. const result = await tryDo(got<AccessTokenResponse>("oauth2/token", {
  50. responseType: "json",
  51. method: "post",
  52. form: opts,
  53. prefixUrl: DISCORD_API,
  54. }));
  55. if (!result.ok) {
  56. if (isHttpError<HTTPError>(result.error)) {
  57. logger.warn("WEB: Failed to authenticate a user: %s", result.error);
  58. return { error: "Couldn't authenticate a user (the session might be old). Please try logging in again.", ok: false };
  59. }
  60. logger.error("WEB: failed to fetch access token: %s", result.error);
  61. return { error: "Unexpected error", ok: false };
  62. }
  63. return { ok: true, ...result.result.body };
  64. }
  65. static async getCurrentUser(token: string): Promise<Option<DiscordUser, {error: string}>> {
  66. const result = await tryDo(got<DiscordUser>("users/@me", {
  67. responseType: "json",
  68. headers: {
  69. Authorization: `Bearer ${token}`,
  70. },
  71. prefixUrl: DISCORD_API,
  72. }));
  73. if (!result.ok) {
  74. if (isHttpError<HTTPError>(result.error)) {
  75. return { error: `Failed to check used ID. Error: ${result.error.message}`, ok: false };
  76. }
  77. return { error: "Unexpected error", ok: false };
  78. }
  79. return { ok: true, ...result.result.body };
  80. }
  81. }