更新ScriptProperty以避免检索重复的Twitter状态

Updating a ScriptProperty to avoid retrieving duplicate Twitter statuses

本文关键字:Twitter 状态 检索 ScriptProperty 更新      更新时间:2023-09-26

我有兴趣写一个推特机器人来帮助当地滑雪场的一些朋友。我从Amit Agarwal那里找到了这个教程,它给了我足够的时间来开始(由于我做了很多修改,它确实花了我5分钟多的时间)。我在谷歌文档上主持这个脚本。

FIRST认为这是javascript(我的理解是谷歌应用程序脚本使用javascript…),到目前为止,当我的代码出现问题时,谷歌搜索javascript之类的东西很有帮助,但如果这实际上不是javascript,请告诉我,这样我就可以相应地更新标签!

我之前没有javascript的经验,所以我很高兴它能真正工作。但我想看看我做得对不对。

start功能启动触发器,触发器每隔一段时间(30分钟)启动fetchTweets()功能。为了避免重复(我遇到的第一个错误)&可能被标记为垃圾邮件,我需要一种方法来确保我不会一次又一次地发布相同的推文。在start()函数中,分配初始since_id值:

ScriptProperties.setProperty("SINCE_TWITTER_ID",        "404251049889759234");

fetchTweet()函数中,我认为我正在用以下语句更新此属性:

ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID + ''n');

这样做好吗?或者有更好/更可靠的方法吗?如果是这样,我怎么能确定它正在更新属性(我可以检查日志文件,它似乎正在执行,所以我可能只需要为记录器创建一个永久文本文件)。

非常感谢您的帮助!!

/**     A  S I M P L E   T W I T T E R   B O T           **/
/**     =======================================          **/
/**     Written by Amit Agarwal @labnol on 03/08/2013    **/
/**     Modified by David Zemens @agnarchy on 11/21/2013 **/
/**     Tutorial link: http://www.labnol.org/?p=27902    **/
/**     Live demo at http://twitter.com/DearAssistant    **/
/**     Last updated on 09/07/2013 - Twitter API Fix     **/

function start() {
  Logger.log("start!" + ''n')
  // REPLACE THESE DUMMY VALUES
  // https://script.google.com/macros/d/18DGYaa-jbaAK9rEv0HZ2cMcWjFGgkvVcvr6TfksMNbbu2Brk3gZeZ46R/edit
  var TWITTER_CONSUMER_KEY     = "___REDACTED___";
  var TWITTER_CONSUMER_SECRET  = "___REDACTED___";
  var TWITTER_HANDLE           = "___REDACTED___";  
  var SEARCH_QUERY             = "___REDACTED___" + TWITTER_HANDLE;
  // Store variables
  ScriptProperties.setProperty("TWITTER_CONSUMER_KEY",    TWITTER_CONSUMER_KEY);
  ScriptProperties.setProperty("TWITTER_CONSUMER_SECRET", TWITTER_CONSUMER_SECRET);
  ScriptProperties.setProperty("TWITTER_HANDLE",          TWITTER_HANDLE);
  ScriptProperties.setProperty("SEARCH_QUERY",            SEARCH_QUERY);
  ScriptProperties.setProperty("SINCE_TWITTER_ID",        "404251049889759234");
  // Delete exiting triggers, if any
  var triggers = ScriptApp.getScriptTriggers();
  for(var i=0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }
  // Setup trigger to read Tweets every 2 hours
  ScriptApp.newTrigger("fetchTweets")
          .timeBased()
          .everyMinutes(30)
          //.everyHours(2)
          .create();
}
function oAuth() {
//Authentication
  var oauthConfig = UrlFetchApp.addOAuthService("twitter");
  oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
  oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
  oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
  oauthConfig.setConsumerKey(ScriptProperties.getProperty("TWITTER_CONSUMER_KEY"));
  oauthConfig.setConsumerSecret(ScriptProperties.getProperty("TWITTER_CONSUMER_SECRET"));
}
function fetchTweets() {
  oAuth();
  // I put this line in to monitor whether the property is getting "stored" so as to avoid
  // reading in duplicate tweets.
  Logger.log("Getting tweets since " + ScriptProperties.getProperty("SINCE_TWITTER_ID"))
  var twitter_handle = ScriptProperties.getProperty("TWITTER_HANDLE");
  var search_query = ScriptProperties.getProperty("SEARCH_QUERY")
  Logger.log("searching tweets to " + search_query + ''n');

  // form the base URL
  // restrict to a certain radius ---:
  //var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&geocode=42.827934,-83.564306,75mi&include_entities=false&result_type=recent&q="; 
  // unrestricted radius:
  var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&include_entities=false&result_type=recent&q="; 
  search = search + encodeString(search_query) + "&since_id=" + ScriptProperties.getProperty("SINCE_TWITTER_ID");    
  var options =
  {
    "method": "get",
    "oAuthServiceName":"twitter",
    "oAuthUseToken":"always"
  };
  try {
    var result = UrlFetchApp.fetch(search, options);    
    var lastID = ScriptProperties.getProperty("SINCE_TWITTER_ID"); 
    if (result.getResponseCode() === 200) {
      var data = Utilities.jsonParse(result.getContentText());
      if (data) {
        var tweets = data.statuses;
        //Logger.log(data.statuses); 
        for (var i=tweets.length-1; i>=0; i--) {
          // Make sure this is a NEW tweet
          if (tweets[i].id > ScriptProperties.getProperty("SINCE_TWITTER_ID")) {
            lastID = (tweets[i].id_str); 
            var answer = tweets[i].text.replace(new RegExp("'@" + twitter_handle, "ig"), "").replace(twitter_handle, "");
            // I find this TRY block may be necessary since a failure to send one of the tweets 
            // may abort the rest of the loop.
            try {
              Logger.log("found >> " + tweets[i].text)
              Logger.log("converted >> " + answer + ''n');
              sendTweet(tweets[i].user.screen_name, tweets[i].id_str, answer.substring(0,140));   
              // Update the script property to avoid duplicates.
              ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID);
              Logger.log("sent to @" + tweets[i].user.screen_name + ''n'); 
            } catch (e) {
              Logger.log(e.toString() + ''n');
            }
           }
        }
      }
    }
  } catch (e) {
    Logger.log(e.toString() + ''n');
  }
    Logger.log("Last used tweet.id: " + lastID + + "'n")
}
function sendTweet(user, reply_id, tweet) {
  var options =
  {
    "method": "POST",
    "oAuthServiceName":"twitter",
    "oAuthUseToken":"always"    
  };
  var status = "https://api.twitter.com/1.1/statuses/update.json";
  status = status + "?status=" + encodeString("RT @" + user + " " + tweet + " - Thanks'!");
  status = status + "&in_reply_to_status_id=" + reply_id;

  try {
    var result = UrlFetchApp.fetch(status, options);
    Logger.log("JSON result = " + result.getContentText() + ''n');    
  }  
  catch (e) {
    Logger.log(e.toString() + ''n');
  }

}
// Thank you +Martin Hawksey - you are awesome
function encodeString (q) {
  // Update: 09/06/2013
  // Google Apps Script is having issues storing oAuth tokens with the Twitter API 1.1 due to some encoding issues.
  // Henc this workaround to remove all the problematic characters from the status message.
  var str = q.replace(/'(/g,'{').replace(/')/g,'}').replace(/'[/g,'{').replace(/']/g,'}').replace(/'!/g, '|').replace(/'*/g, 'x').replace(/''/g, '');
  return encodeURIComponent(str);
//   var str =  encodeURIComponent(q);
//   str = str.replace(/!/g,'%21');
//   str = str.replace(/'*/g,'%2A');
//   str = str.replace(/'(/g,'%28');
//   str = str.replace(/')/g,'%29');
//   str = str.replace(/'/g,'%27');
//   return str;
}

使用ScriptProperties.setProperty("KEY", "VALUE");时,内部脚本属性将覆盖重复的键(即,如果旧属性具有相同的键,则新属性将替换它)。因此,在您的情况下,由于您对键(SINCE_TWITTER_ID)使用相同的标识符,因此替换该键的任何先前脚本属性。

此外,您还可以通过File->Project properties->Project properties (tab)查看脚本属性。伊莫谷歌的名字不太好特定于谷歌用户的用户属性编写特定于正在处理的脚本项目的脚本属性

此外,在设置属性时,在值中包含'n可能不是一个好主意。这将导致未来出现各种各样的错误,因为你必须与以下内容进行比较:

var valToCompare = "My value'n";

而不是:

var valToCompare = "My value";

因为在调用fetchTweet()函数后,SINCE_TWITTER_ID中的值实际上将是"some value'n"

当然,我认为这似乎更合乎逻辑,除非你真的需要换行符(在这种情况下,你应该在其他地方使用它们,用于这个应用程序)。

你这样也没关系,我不知道你为什么在末尾添加。可能会混淆其他代码。您可以在脚本的文件菜单中看到脚本属性+属性