GASでtwitterapiを使用して動画を投稿したい。

gSS

1const initEndpoint = "https://upload.twitter.com/1.1/media/upload.json"; 2const appendEndpoint = "https://upload.twitter.com/1.1/media/upload.json"; 3const finalizeEndpoint = "https://upload.twitter.com/1.1/media/upload.json"; 4const tweetEndpoint = "https://api.twitter.com/2/tweets"; 5 6function listVideosInFolder() { 7 var folderid = '1zp0wOARVqgQOcczjDcSizitx7LbO3K7c'; 8 var folder = DriveApp.getFolderById(folderid); 9 var files = folder.getFiles(); 10 11 var videoFiles = []; 12 while (files.hasNext()) { 13 var file = files.next(); 14 if (file.getMimeType().startsWith('video/')) { 15 videoFiles.push({ 16 name: file.getName(), 17 url: file.getUrl(), 18 id: file.getId() 19 }); 20 } 21 } 22 23 videoFiles.forEach(function(video) { 24 Logger.log('Name: ' + video.name + ', URL: ' + video.url); 25 }); 26 return videoFiles[0].url; 27} 28 29function postTwitter() { 30 var videoUrl = listVideosInFolder(); 31 var videoBlob = UrlFetchApp.fetch(videoUrl).getBlob(); 32 var videoSize = videoBlob.getBytes().length; 33 34 let twitterText = "1####Test tweet!!!"; // ツイートしたいメッセージを入力 35 let service = getService(); 36 37 if (service.hasAccess()) { 38 const headers = { 39 Authorization: 'Bearer ' + service.getAccessToken(), 40 'Content-Type': 'application/json' 41 }; 42 43 // メディアアップロード - INIT 44 var initPayload = { 45 command: "INIT", 46 total_bytes: videoSize, 47 media_type: "video/mp4", 48 media_category: "tweet_video" 49 }; 50 51 var initResponse = UrlFetchApp.fetch(initEndpoint, { 52 method: "post", 53 headers: headers, 54 payload: JSON.stringify(initPayload), 55 muteHttpExceptions: true 56 }); 57 58 if (initResponse.getResponseCode() !== 200) { 59 Logger.log("Code: " + initResponse.getResponseCode()); 60 Logger.log("INIT failed: " + initResponse.getContentText()); 61 return; 62 } 63 var initResult = JSON.parse(initResponse.getContentText()); 64 var mediaId = initResult.media_id_string; 65 66 // メディアアップロード - APPEND 67 var chunkSize = 5 * 1024 * 1024; // 5MB 68 var bytes = videoBlob.getBytes(); 69 for (var i = 0; i < bytes.length; i += chunkSize) { 70 var chunk = bytes.slice(i, i + chunkSize); 71 var formData = { 72 command: "APPEND", 73 media_id: mediaId, 74 segment_index: i / chunkSize, 75 media: Utilities.newBlob(chunk).getBytes() 76 }; 77 78 var appendResponse = UrlFetchApp.fetch(appendEndpoint, { 79 method: "post", 80 headers: headers, 81 payload: formData, 82 muteHttpExceptions: true 83 }); 84 85 if (appendResponse.getResponseCode() !== 200) { 86 Logger.log("Append failed: " + appendResponse.getContentText()); 87 return; 88 } 89 } 90 91 // メディアアップロード - FINALIZE 92 var finalizePayload = { 93 command: "FINALIZE", 94 media_id: mediaId 95 }; 96 97 var finalizeResponse = UrlFetchApp.fetch(finalizeEndpoint, { 98 method: "post", 99 headers: headers, 100 payload: JSON.stringify(finalizePayload), 101 muteHttpExceptions: true 102 }); 103 var finalizeResult = JSON.parse(finalizeResponse.getContentText()); 104 105 if (finalizeResponse.getResponseCode() !== 200 || !finalizeResult.media_id_string) { 106 Logger.log("Finalize failed: " + finalizeResponse.getContentText()); 107 return; 108 } 109 110 // ツイート投稿 111 let tweetPayload = { 112 text: twitterText, 113 media: { 114 media_ids: [mediaId] 115 } 116 }; 117 118 const tweetResponse = UrlFetchApp.fetch(tweetEndpoint, { 119 method: "post", 120 headers: headers, 121 payload: JSON.stringify(tweetPayload), 122 contentType: "application/json", 123 muteHttpExceptions: true 124 }); 125 const tweetContentText = tweetResponse.getContentText(); 126 Logger.log("Tweet response: " + tweetContentText); 127 128 try { 129 var result = JSON.parse(tweetContentText); 130 } catch (e) { 131 Logger.log("Failed to parse tweet response: " + tweetContentText); 132 Logger.log(e.toString()); 133 return; 134 } 135 136 Logger.log(JSON.stringify(result, null, 2)); 137 138 } else { 139 Logger.log("Not Authorized"); 140 } 141} 142 143function main() { 144 const service = getService(); 145 if (service.hasAccess()) { 146 Logger.log("Already authorized"); 147 } else { 148 const authorizationUrl = service.getAuthorizationUrl(); 149 Logger.log('Open the following URL and re-run the script: %s', authorizationUrl); 150 } 151} 152 153function getService() { 154 pkceChallengeVerifier(); 155 const userProps = PropertiesService.getUserProperties(); 156 const scriptProps = PropertiesService.getScriptProperties(); 157 const clientId = scriptProps.getProperty('CLIENT_ID'); 158 const clientSecret = scriptProps.getProperty('CLIENT_SECRET'); 159 160 return OAuth2.createService('twitter') 161 .setAuthorizationBaseUrl('https://twitter.com/i/oauth2/authorize') 162 .setTokenUrl('https://api.twitter.com/2/oauth2/token?code_verifier=' + userProps.getProperty("code_verifier")) 163 .setClientId(clientId) 164 .setClientSecret(clientSecret) 165 .setCallbackFunction('authCallback') 166 .setPropertyStore(userProps) 167 .setScope('users.read tweet.read tweet.write offline.access') 168 .setParam('response_type', 'code') 169 .setParam('code_challenge_method', 'S256') 170 .setParam('code_challenge', userProps.getProperty("code_challenge")) 171 .setTokenHeaders({ 172 'Authorization': 'Basic ' + Utilities.base64Encode(clientId + ':' + clientSecret), 173 'Content-Type': 'application/x-www-form-urlencoded' 174 }); 175} 176 177function authCallback(request) { 178 const service = getService(); 179 const authorized = service.handleCallback(request); 180 if (authorized) { 181 return HtmlService.createHtmlOutput('Success!'); 182 } else { 183 return HtmlService.createHtmlOutput('Denied.'); 184 } 185} 186 187function pkceChallengeVerifier() { 188 var userProps = PropertiesService.getUserProperties(); 189 if (!userProps.getProperty("code_verifier")) { 190 var verifier = ""; 191 var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"; 192 193 for (var i = 0; i < 128; i++) { 194 verifier += possible.charAt(Math.floor(Math.random() * possible.length)); 195 } 196 197 var sha256Hash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, verifier); 198 199 var challenge = Utilities.base64Encode(sha256Hash) 200 .replace(/\+/g, '-') 201 .replace(/\//g, '_') 202 .replace(/=+$/, ''); 203 userProps.setProperty("code_verifier", verifier); 204 userProps.setProperty("code_challenge", challenge); 205 } 206} 207 208function logRedirectUri() { 209 var service = getService(); 210 Logger.log(service.getRedirectUri()); 211} 212

コメントを投稿

0 コメント