Skip to content

Commit

Permalink
feat: Add oauth & users/me endpoint (#105)
Browse files Browse the repository at this point in the history
* feat(oauth,melookup): oauthとusers/meエンドポイントを追加

* build(deps): update std
  • Loading branch information
kamekyame committed Sep 30, 2023
1 parent d6a6abc commit f4dede6
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 2 deletions.
22 changes: 22 additions & 0 deletions api_v2/data_interface/tweet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,25 @@ export interface IncludesObject {
media?: MediaObject[];
polls?: PollObject;
}

export interface SelectTweetFields {
"attachments"?: boolean;
"author_id"?: boolean;
"context_annotations"?: boolean;
"conversation_id"?: boolean;
"created_at"?: boolean;
"entities"?: boolean;
"geo"?: boolean;
"id"?: boolean;
"in_reply_to_user_id"?: boolean;
"lang"?: boolean;
"non_public_metrics"?: boolean;
"public_metrics"?: boolean;
"organic_metrics"?: boolean;
"promoted_metrics"?: boolean;
"possibly_sensitive"?: boolean;
"referenced_tweets"?: boolean;
"source"?: boolean;
"text"?: boolean;
"withheld"?: boolean;
}
20 changes: 19 additions & 1 deletion api_v2/data_interface/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface UserObject {
entities?: Entities;
location?: string;
pinned_tweet_id?: string;
profile_image_url: string;
profile_image_url?: string;
protected?: boolean;
public_metrics?: {
followers_count: number;
Expand All @@ -22,3 +22,21 @@ export interface UserObject {
verified?: boolean;
withheld?: WithHeld;
}

export interface SelectUserFields {
"created_at"?: boolean;
"description"?: boolean;
"entities"?: boolean;
"id"?: boolean;
"location"?: boolean;
"name"?: boolean;
"pinned_tweet_id"?: boolean;
"profile_image_url"?: boolean;
"protected"?: boolean;
"public_metrics"?: boolean;
"url"?: boolean;
"username"?: boolean;
"verified"?: boolean;
"verified_type"?: boolean;
"withheld"?: boolean;
}
41 changes: 41 additions & 0 deletions api_v2/users/lookup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { addParamOption, endpoints, getUrl } from "../../util.ts";

import { SelectTweetFields, TweetObject } from "../data_interface/tweet.ts";
import { SelectUserFields, UserObject } from "../data_interface/user.ts";

interface UsersMeParam {
"expansions"?: {
"pinned_tweet_id"?: boolean;
};
"tweet.fields"?: SelectTweetFields;
"user.fields"?: SelectUserFields;
}

interface UsersMeResponse {
data: UserObject;
includes?: {
tweets?: TweetObject[];
};
}

/**
* Returns information about an authorized user.
*
* https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me
*/
export async function getUsersMe(auth: string, param: UsersMeParam) {
const url = getUrl(endpoints.api_v2.users.me);
//url.searchParams.set("ids", param.ids);
addParamOption(url, param);
console.log(url);
const res = await (await fetch(
url.toString(),
{
headers: new Headers({
"Authorization": `Bearer ${auth}`,
}),
},
)).json();

return res as UsersMeResponse;
}
124 changes: 124 additions & 0 deletions auth/oauth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { oAuth1Fetch, OAuth1Info } from "../deps.ts";

import { endpoints, getUrl } from "../util.ts";

type RequestTokenParam = {
oauth_callback: string;
x_auth_access_type?: "read" | "write";
};
type AuthenticationParam = {
oauth_token: string;
force_login?: boolean;
screen_name?: string;
};
type AuthorizeParam = AuthenticationParam;
type AccessTokenParam = {
oauth_token: string;
oauth_verifier: string;
};

/**
* **Step 1** of the 3-legged OAuth flow and Sign in with Twitter
*
* Allows a Consumer application to obtain an OAuth Request Token to request user authorization.
*
* https://developer.twitter.com/en/docs/authentication/api-reference/request_token
*/
export async function requestOAuthToken(
auth: OAuth1Info,
param: RequestTokenParam,
) {
const url = getUrl(endpoints.oauth.requestToken);
url.searchParams.set("oauth_callback", param.oauth_callback);
if (param.x_auth_access_type) {
url.searchParams.set("x_auth_access_type", param.x_auth_access_type);
}
const res = await oAuth1Fetch(auth, url);
if (res.ok === false) {
throw new Error("Failed to request OAuth token");
}

const bodyText = await res.text();
const bodySp = new URLSearchParams(bodyText);
const oauth_token = bodySp.get("oauth_token") as string;
const oauth_token_secret = bodySp.get("oauth_token_secret") as string;
const oauth_callback_confirmed =
bodySp.get("oauth_callback_confirmed") === "true" ? true : false;

return { oauth_token, oauth_token_secret, oauth_callback_confirmed };
}

/**
* **Step 2** of the 3-legged OAuth flow and Sign in with Twitter
*
* Allows a Consumer application to use an OAuth Request Tokento request user authorization.
*
* https://developer.twitter.com/en/docs/authentication/api-reference/authenticate
*/
export function getAuthenticateUrl(param: AuthenticationParam) {
const url = getUrl(endpoints.oauth.authenticate);
url.searchParams.set("oauth_token", param.oauth_token);
if (param.force_login !== undefined) {
url.searchParams.set("force_login", param.force_login.toString());
}
if (param.screen_name) {
url.searchParams.set("screen_name", param.screen_name);
}

return url;
}

/**
* **Step 2** of the 3-legged OAuth flow and Sign in with Twitter
*
* Allows a Consumer application to use an OAuth Request Tokento request user authorization.
*
* https://developer.twitter.com/en/docs/authentication/api-reference/authorize
*/
export function getAuthorizeUrl(param: AuthorizeParam) {
const url = getUrl(endpoints.oauth.authorize);
url.searchParams.set("oauth_token", param.oauth_token);
if (param.force_login !== undefined) {
url.searchParams.set("force_login", param.force_login.toString());
}
if (param.screen_name) {
url.searchParams.set("screen_name", param.screen_name);
}

return url;
}

/**
* **Step 3** of the 3-legged OAuth flow and Sign in with Twitter
*
* Allows a Consumer application to exchange the OAuth Request Token for an OAuth Access Token.
*
* https://developer.twitter.com/en/docs/authentication/api-reference/access_token
*/
export async function getOAuthAccessToken(param: AccessTokenParam) {
const url = getUrl(endpoints.oauth.accessToken);
url.searchParams.set("oauth_token", param.oauth_token);
url.searchParams.set("oauth_verifier", param.oauth_verifier);

const res = await fetch(url);
const a = new URLSearchParams(await res.text());
const oauth_token = a.get("oauth_token") as string;
const oauth_token_secret = a.get("oauth_token_secret") as string;
const user_id = a.get("user_id") as string;
const screen_name = a.get("screen_name") as string;

return {
oauth_token,
oauth_token_secret,
user_id,
screen_name,
};
}

export async function invalidateOAuthToken(auth: OAuth1Info) {
const url = getUrl(endpoints.oauth.invalidateToken);
const res = await oAuth1Fetch(auth, url);
if (res.ok === false) {
throw new Error("invalidateToken failed");
}
}
2 changes: 1 addition & 1 deletion deps-test.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "https://deno.land/std@0.165.0/testing/asserts.ts";
export * from "https://deno.land/std@0.203.0/testing/asserts.ts";
2 changes: 2 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export * from "./api_v1/tweets/search.ts";

export * from "./api_v2/tweets/filtered_stream.ts";
export * from "./api_v2/tweets/lookup.ts";
export * from "./api_v2/users/lookup.ts";

export * from "./auth/oauth.ts";
export * from "./auth/oauth2.ts";
export * from "./util.ts";
10 changes: 10 additions & 0 deletions util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ export const setHost = (url: string) => {
};

export const endpoints = {
oauth: {
requestToken: "/oauth/request_token",
authenticate: "/oauth/authenticate",
authorize: "/oauth/authorize",
accessToken: "/oauth/access_token",
invalidateToken: "/oauth/invalidate_token",
},
outh2: {
getToken: "/oauth2/token",
},
Expand Down Expand Up @@ -32,6 +39,9 @@ export const endpoints = {
rules: "/2/tweets/search/stream/rules",
connect: "/2/tweets/search/stream",
},
users: {
me: "/2/users/me",
},
},
};

Expand Down

0 comments on commit f4dede6

Please sign in to comment.