001/* 002 * Copyright (c) 2010-2024 Mark Allen, Norbert Bartels. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 * THE SOFTWARE. 021 */ 022package com.restfb; 023 024import static com.restfb.util.ObjectUtil.verifyParameterPresence; 025 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029 030import com.restfb.exception.FacebookResponseContentException; 031import com.restfb.scope.ScopeBuilder; 032 033/** 034 * A class representing a client for interacting with Facebook's Threads API. This class extends the 035 * DefaultFacebookClient and provides additional methods for accessing Threads-specific functionalities. 036 */ 037public class DefaultThreadsClient extends DefaultFacebookClient { 038 039 public DefaultThreadsClient(Version version) { 040 super(version); 041 } 042 043 public DefaultThreadsClient(String accessToken, Version apiVersion) { 044 super(accessToken, apiVersion); 045 } 046 047 public DefaultThreadsClient(String accessToken, String appSecret, Version apiVersion) { 048 super(accessToken, appSecret, apiVersion); 049 } 050 051 public DefaultThreadsClient(String accessToken, WebRequestor webRequestor, JsonMapper jsonMapper, 052 Version apiVersion) { 053 super(accessToken, webRequestor, jsonMapper, apiVersion); 054 } 055 056 public DefaultThreadsClient(String accessToken, String appSecret, WebRequestor webRequestor, JsonMapper jsonMapper, 057 Version apiVersion) { 058 super(accessToken, appSecret, webRequestor, jsonMapper, apiVersion); 059 } 060 061 @Override 062 public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, String state, 063 Parameter... parameters) { 064 List<Parameter> parameterList = new ArrayList<>(); 065 Collections.addAll(parameterList, parameters); 066 parameterList.add(Parameter.with("response_type", CODE)); 067 return getGenericLoginDialogUrl(appId, redirectUri, scope, 068 () -> getFacebookEndpointUrls().getThreadsBaseEndpoint() + "/oauth/authorize", state, parameterList); 069 } 070 071 @Override 072 public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, Parameter... parameters) { 073 return getLoginDialogUrl(appId, redirectUri, scope, null, parameters); 074 } 075 076 @Override 077 public AccessToken obtainUserAccessToken(String clientId, String clientSecret, String redirectUri, String code) { 078 verifyParameterPresence(CLIENT_ID, clientId); 079 verifyParameterPresence(PARAM_CLIENT_SECRET, clientSecret); 080 verifyParameterPresence(CODE, code); 081 verifyParameterPresence(REDIRECT_URI, redirectUri); 082 083 return publish(PATH_OAUTH_ACCESS_TOKEN, AccessToken.class, // 084 Parameter.with(CLIENT_ID, clientId), // 085 Parameter.with(PARAM_CLIENT_SECRET, clientSecret), // 086 Parameter.with(CODE, code), // 087 Parameter.with(GRANT_TYPE, "authorization_code"), // 088 Parameter.with(REDIRECT_URI, redirectUri)); 089 } 090 091 @Override 092 public AccessToken obtainExtendedAccessToken(String appId, String appSecret, String accessToken) { 093 throw new UnsupportedOperationException("Not supported, use the other obtainExtendedAccessToken instead"); 094 } 095 096 @Override 097 public AccessToken obtainExtendedAccessToken(String appId, String appSecret) { 098 verifyParameterPresence(APP_SECRET, appSecret); 099 verifyParameterPresence("accessToken", accessToken); 100 101 String response = makeRequest("access_token", false, false, null, // 102 Parameter.with(PARAM_CLIENT_SECRET, appSecret), // 103 Parameter.with(GRANT_TYPE, "th_exchange_token"), // 104 Parameter.withFields("access_token,expires_in,token_type")); 105 106 try { 107 return getAccessTokenFromResponse(response); 108 } catch (Exception t) { 109 throw new FacebookResponseContentException(CANNOT_EXTRACT_ACCESS_TOKEN_MESSAGE, t); 110 } 111 } 112 113 /** 114 * Obtain a refreshed Threads extended access token. 115 * 116 * <p> 117 * This method is used to refresh an existing Threads extended access token. Extended access tokens expire after a 118 * certain period of time, and this method allows you to obtain a new one using the refresh token provided with the 119 * original extended access token. 120 * 121 * @return A new {@link AccessToken} object containing the refreshed access token, expiration time, and token type. 122 * @throws FacebookResponseContentException 123 * If the response from the Facebook API cannot be parsed or if the access token cannot be extracted from 124 * the response. 125 */ 126 @Override 127 public AccessToken obtainRefreshedExtendedAccessToken() { 128 String response = makeRequest("refresh_access_token", false, false, null, // 129 Parameter.with(GRANT_TYPE, "th_refresh_token"), // 130 Parameter.withFields("access_token,expires_in,token_type")); 131 try { 132 return getAccessTokenFromResponse(response); 133 } catch (Exception t) { 134 throw new FacebookResponseContentException(CANNOT_EXTRACT_ACCESS_TOKEN_MESSAGE, t); 135 } 136 } 137 138 @Override 139 public FacebookClient createClientWithAccessToken(String accessToken) { 140 return new DefaultThreadsClient(accessToken, this.appSecret, getWebRequestor(), getJsonMapper(), this.apiVersion); 141 } 142 143 @Override 144 protected String createBaseUrlForEndpoint(String apiCall, boolean hasAttachment, boolean hasReel) { 145 return getThreadsGraphEndpointUrl(); 146 } 147 148 private String getThreadsGraphEndpointUrl() { 149 if (apiVersion.isUrlElementRequired()) { 150 return getFacebookEndpointUrls().getThreadsApiEndpoint() + '/' + apiVersion.getUrlElement(); 151 } else { 152 return getFacebookEndpointUrls().getThreadsApiEndpoint(); 153 } 154 } 155}