-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdeleteTweets.gs
298 lines (271 loc) · 10.4 KB
/
deleteTweets.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//you must configure
var TWITTER_USER = 'YOUR TWITTER USER NAME';
var MAX_AGE_IN_DAYS = 7; //0 to delete all tweets
var SAVE_THIS_TWEET = 0;
//From apps.twitter.com
var CONSUMER_KEY = 'YOUR CONSUMER KEY';
var CONSUMER_SECRET = 'YOUR CONSUMER CONSUMER_SECRET';
var ACCESS_TOKEN = 'YOUR ACCESS TOKEN';
var ACCESS_SECRET = 'YOUR ACCESS SECRET';
//From Google Drive
var TWEET_JS_FILE = 'TWEETS.JS FILE ID';
//You shouldn't have to change anything below this line
var NOW_DATE = new Date();
var authUrlFetch;
var destroy_tweets = 0;
function deleteTweets() {
//var connection = getTwitterService();
var file = DriveApp.getFileById(TWEET_JS_FILE);
var content = file.getBlob().getDataAsString();
var data = JSON.parse(content.replace("window.YTD.tweet.part0 = ", ""));//Parse file as json array
var tweetsToDelete = data.length;
console.log("Total Tweets to delete: " + data.length);
if (data.length > 1000) { tweetsToDelete = 1000; }
for(var i = tweetsToDelete - 1; i >= 0; i--){
var tweet = data[i];
var tweet_id = tweet.id;
var tweet_age = parseInt((NOW_DATE - new Date(tweet.created_at))/1000/60/60/24);
if (tweet_age > MAX_AGE_IN_DAYS && tweet_id != SAVE_THIS_TWEET) {
console.log('Deleting ' + tweet_id);
destroy_tweets++;
if (destroy(tweet_id)) {
console.log('Deleted ' + tweet_id + " - " + tweet_age + " days old. Tweets deleted: " + destroy_tweets);
//remove tweet from JSON
data.splice(i, 1)
}
}
}
console.log("Update .js file with " + data.length + " tweets.");
file.setContent(JSON.stringify(data));
}
function destroy(tweet_id) {
var options = {
method: "POST",
payload: { id: tweet_id }
};
try {
var result = authUrlFetch.fetch('https://api.twitter.com/1.1/statuses/destroy/' + tweet_id + '.json', '', options);
return(true);
}
catch (e) {
console.log('Error deleting ' + tweet_id);
console.log(e.toString());
return(false);
}
}
function getTwitterService() {
if (typeof OAuth1 === 'undefined') {
var libUrl = 'https://developers.google.com/adwords/scripts/docs/examples/oauth10-library';
throw Error('OAuth1 library not found. Please take a copy of the OAuth1 ' +
'library from ' + libUrl + ' and append to the bottom of this script.');
}
authUrlFetch = OAuth1.withAccessToken(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_SECRET);
}
/**
* Adds a OAuth1 object to the global scope. This can be used as follows:
*
* var urlFetch = OAuth1.withAccessToken(consumerKey, consumerSecret,
* accessToken, accessSecret);
* var response = urlFetch.fetch(url, params, options);
*/
(function(scope) {
/**
* Creates an object to provide OAuth1-based requests to API resources.
* @param {string} consumerKey
* @param {string} consumerSecret
* @param {string} accessToken
* @param {string} accessSecret
* @constructor
*/
function OAuth1UrlFetchApp(
consumerKey, consumerSecret, accessToken, accessSecret) {
this.consumerKey_ = consumerKey;
this.consumerSecret_ = consumerSecret;
this.accessToken_ = accessToken;
this.accessSecret_ = accessSecret;
}
/**
* Sends a signed OAuth 1.0 request.
* @param {string} url The URL of the API resource.
* @param {?Object.<string>=} opt_params Map of parameters for the URL.
* @param {?Object.<string>=} opt_options Options for passing to UrlFetchApp
* for example, to set the method to POST, or to include a form body.
* @return {?Object} The resulting object on success, or null if a failure.
*/
OAuth1UrlFetchApp.prototype.fetch = function(url, opt_params, opt_options) {
var oauthParams = {
'oauth_consumer_key': this.consumerKey_,
'oauth_timestamp': parseInt(new Date().getTime() / 1000),
'oauth_nonce': this.generateNonce_(),
'oauth_version': '1.0',
'oauth_token': this.accessToken_,
'oauth_signature_method': 'HMAC-SHA1'
};
var method = 'GET';
if (opt_options && opt_options.method) {
method = opt_options.method;
}
if (opt_options && opt_options.payload) {
var formPayload = opt_options.payload;
}
var requestString =
this.generateRequestString_(oauthParams, opt_params, formPayload);
var signatureBaseString =
this.generateSignatureBaseString_(method, url, requestString);
var signature = Utilities.computeHmacSignature(
Utilities.MacAlgorithm.HMAC_SHA_1, signatureBaseString,
this.getSigningKey_());
var b64signature = Utilities.base64Encode(signature);
oauthParams['oauth_signature'] = this.escape_(b64signature);
var fetchOptions = opt_options || {};
fetchOptions['headers'] = {
Authorization: this.generateAuthorizationHeader_(oauthParams)
};
if (fetchOptions.payload) {
fetchOptions.payload = this.escapeForm_(fetchOptions.payload);
}
return UrlFetchApp.fetch(
this.joinUrlToParams_(url, opt_params), fetchOptions);
};
/**
* Concatenates request URL to parameters to form a single string.
* @param {string} url The URL of the resource.
* @param {?Object.<string>=} opt_params Optional key/value map of parameters.
* @return {string} The full path built out with parameters.
*/
OAuth1UrlFetchApp.prototype.joinUrlToParams_ = function(url, opt_params) {
if (!opt_params) {
return url;
}
var paramKeys = Object.keys(opt_params);
var paramList = [];
for (var i = 0, paramKey; paramKey = paramKeys[i]; i++) {
paramList.push([paramKey, opt_params[paramKey]].join('='));
}
return url + '?' + paramList.join('&');
};
/**
* Generates a random nonce for use in the OAuth request.
* @return {string} A random string.
*/
OAuth1UrlFetchApp.prototype.generateNonce_ = function() {
return Utilities
.base64Encode(Utilities.computeDigest(
Utilities.DigestAlgorithm.SHA_1,
parseInt(Math.floor(Math.random() * 10000))))
.replace(/[\/=_+]/g, '');
};
/**
* Creates a properly-formatted string from a map of key/values from a form
* post.
* @param {!Object.<string>} payload Map of key/values.
* @return {string} The formatted string for the body of the POST message.
*/
OAuth1UrlFetchApp.prototype.escapeForm_ = function(payload) {
var escaped = [];
var keys = Object.keys(payload);
for (var i = 0, key; key = keys[i]; i++) {
escaped.push([this.escape_(key), this.escape_(payload[key])].join('='));
}
return escaped.join('&');
};
/**
* Returns a percent-escaped string for use with OAuth. Note that
* encodeURIComponent is not sufficient for this as the Twitter API expects
* characters such as exclamation-mark to be encoded. See:
* https://dev.twitter.com/discussions/12378
* @param {string} str The string to be escaped.
* @return {string} The escaped string.
*/
OAuth1UrlFetchApp.prototype.escape_ = function(str) {
return encodeURIComponent(str).replace(/[!*()']/g, function(v) {
return '%' + v.charCodeAt().toString(16);
});
};
/**
* Generates the Authorization header using the OAuth parameters and
* calculated signature.
* @param {!Object} oauthParams A map of the required OAuth parameters. See:
* https://dev.twitter.com/oauth/overview/authorizing-requests
* @return {string} An Authorization header value for use in HTTP requests.
*/
OAuth1UrlFetchApp.prototype.generateAuthorizationHeader_ = function(
oauthParams) {
var params = [];
var keys = Object.keys(oauthParams).sort();
for (var i = 0, key; key = keys[i]; i++) {
params.push(key + '="' + oauthParams[key] + '"');
}
return 'OAuth ' + params.join(', ');
};
/**
* Generates the signature string for the request.
* @param {string} method The HTTP method e.g. GET, POST
* @param {string} The URL.
* @param {string} requestString The string representing the parameters to the
* API call as constructed by generateRequestString.
* @return {string} The signature base string. See:
* https://dev.twitter.com/oauth/overview/creating-signatures
*/
OAuth1UrlFetchApp.prototype.generateSignatureBaseString_ = function(
method, url, requestString) {
return [method, this.escape_(url), this.escape_(requestString)].join('&');
};
/**
* Generates the key for signing the OAuth request
* @return {string} The signing key.
*/
OAuth1UrlFetchApp.prototype.getSigningKey_ = function() {
return this.escape_(this.consumerSecret_) + '&' +
this.escape_(this.accessSecret_);
};
/**
* Generates the request string for signing, as used to produce a signature
* for the Authorization header. see:
* https://dev.twitter.com/oauth/overview/creating-signatures
* @param {!Object} oauthParams The required OAuth parameters for the request,
* see: https://dev.twitter.com/oauth/overview/authorizing-requests
* @param {?Object=} opt_params Optional parameters specified as part of the
* request, in map form, for example to specify /path?a=b&c=d&e=f... etc
* @param {?Object=} opt_formPayload Optional mapping of pairs used in a form
* as part of a POST request.
* @return {string} The request string
*/
OAuth1UrlFetchApp.prototype.generateRequestString_ = function(
oauthParams, opt_params, opt_formPayload) {
var requestParams = {};
var requestPath = [];
for (var i = 0; i < arguments.length; i++) {
var mapping = arguments[i];
if (mapping) {
var paramKeys = Object.keys(mapping);
for (var j = 0, paramKey; paramKey = paramKeys[j]; j++) {
requestParams[paramKey] = mapping[paramKey];
}
}
}
var requestKeys = Object.keys(requestParams);
requestKeys.sort();
for (var m = 0, requestKey; requestKey = requestKeys[m]; m++) {
requestPath.push([
this.escape_(requestKey), this.escape_(requestParams[requestKey])
].join('='));
}
return requestPath.join('&');
};
/**
* Builds a OAuth1UrlFetchApp object based on supplied access token (and other
* parameters.
* @param {string} consumerKey
* @param {string} consumerSecret
* @param {string} accessToken
* @param {string} accessSecret
* @return {!OAuth1UrlFetchApp}
*/
function withAccessToken(
consumerKey, consumerSecret, accessToken, accessSecret) {
return new OAuth1UrlFetchApp(
consumerKey, consumerSecret, accessToken, accessSecret);
}
scope.OAuth1 = {withAccessToken: withAccessToken};
})(this);