显示带有标签的帖子 高级. 显示所有帖子
显示带有标签的帖子 高级. 显示所有帖子

2013年5月3日,星期五

使用辽宁福利彩票中心将AdWords数据放入Amazon S3

我不确定您的身份,但是编写所有这些辽宁福利彩票中心会使我陷入Google Spreadsheet重载的境地。随着访问任何AdWords报告功能的发布,数据很快开始变得难以处理。一旦事情超过了数千行,我就更喜欢使用Excel或在数据库中存储和处理数据。

因此,我在寻找其他方法来从辽宁福利彩票中心世界中的AdWords报告中获取数据,因此我想到了 亚马逊S3。亚马逊通过其亚马逊网络服务提供了RESTful API,使您可以从云中存储和检索大量数据。我认为我可以使用它们存储帐户/广告系列/关键字性能的一些CSV文件,以便以后通过其他辽宁福利彩票中心或软件进行下载或处理。

下面的辽宁福利彩票中心尝试封装构建和授权适当的S3放置请求所需的所有逻辑。我遵循找到的文档 这里.

要免费开始使用,请注册 亚马逊S3在这里 并在此处找到您的访问权限和密钥:


接下来,创建一个新的S3存储桶以通过AWS控制台保存所有数据。填写以下详细信息,然后尝试测试文件。理想情况下,您可以将其与其他AdWords之一结合使用 报告 剧本 在这里找到开始将您的数据存储在云中。

注意:这是一组非常特定的功能,代表将文件放入Amazon S3所需的最低限度信息。有关更多信息,请查看完整的 亚马逊S3文件.

Thanks,
拉斯

//-----------------------------------
// Put Data To 亚马逊S3
// Created By: 拉斯 Savage
// FreeAdWordsScripts.com
//-----------------------------------
var ACCESS_KEY = 'YOUR_ACCESS_KEY_HERE';
var SECRET_KEY = 'YOUR_SECRET_KEY_HERE';
var S3_BUCKET = 'YOUR_S3_BUCKET_NAME_HERE';
  

function main() {
  var date_str = Utilities.formatDate(new Date(),AdWordsApp.currentAccount().getTimeZone(),'yyyy-MM-dd');
  var file_name = 'adwords_keyword_perf_'+date_str+'.csv';
  putDataToBucket(S3_BUCKET,'/'+file_name,'this is where the data from an AdWords report would go.');
}


function putDataToBucket(bucket,file_path,data) {
  var auth_options = {  
    method : 'PUT',
    base_url : "http://" + bucket + ".s3.amazonaws.com",
    s3_bucket : bucket,
    path : file_path,
    headers : { 
       "Date" : getDate(),
       "Content-Type" : "application/x-www-form-urlencoded"
    },
  };
  
  var auth_string = generateAuthString(auth_options);
  auth_options.headers["Authorization"] = auth_string;
  
  var options = {
    method : auth_options.method,
    headers : auth_options.headers,
    payload : data
  };
  
  return (UrlFetchApp.fetch(auth_options.base_url+auth_options.path, options).getResponseCode() == 200);
}

//Generates an AWS Auth String
//For more info, see the AWS docs - http://goo.gl/m5nCe
function generateAuthString(url) {
  var string_to_sign =  getStringToSign(url);
  Logger.log(string_to_sign);
  var signature = getSignature(SECRET_KEY,string_to_sign);
  return "AWS" + " " + ACCESS_KEY + ":" + signature;
}

//Generates an AWS Signature
//For more info, see the AWS docs - http://goo.gl/m5nCe
function getSignature(SECRET_KEY,string_to_sign) {
  return  Utilities.base64Encode(
    Utilities.computeHmacSignature(
      Utilities.MacAlgorithm.HMAC_SHA_1,
      string_to_sign,
      SECRET_KEY,
      Utilities.Charset.UTF_8
    )
  );
}

//Generates an AWS string to sign
//For more info, see the AWS docs - http://goo.gl/m5nCe
function getStringToSign(url,params) {
  var method = url.method;
  var date = url.headers.Date;
  
  return method + "\n" + "\n" + 
    url.headers['Content-Type'] + "\n" +
    date + "\n" + 
    getCanonicalizedAmzHeaders(url);
}

//Generates the Canonicalized Amazon Headers (not really)
//For more info, see the AWS docs - http://goo.gl/m5nCe
function getCanonicalizedAmzHeaders(url) {
  var ret_val = "/" + url.s3_bucket;
  ret_val += url.path;
  return ret_val;
}

function getDate() {
  return Utilities.formatDate(new Date(),"GMT", "EEE, dd MMM yyyy HH:mm:ss +0000");
}

2013年4月5日,星期五

动态调整广告系列预算v2.0

2013年4月6日更新:请确保将每日预算乘以30.5,以将其转化为每月预算(第150行)。

2013年6月6日更新:请确保在重置预算后,将预算除以30.5,以将其转化为每日预算(第95行)。

更新2013-04-07:非常感谢FoxSUP帮助我跟踪更新预算的问题。修复了第78行,将当前预算乘以1 + to_change,而不仅仅是to_change。还修复了计算更改时的错误(第56行)。


这是我放在一起的上一个辽宁福利彩票中心的更新 动态调整广告系列预算。一些评论提出了一些要求,我认为将它们合并到一个新帖子中会更容易。

现在,该辽宁福利彩票中心将跟踪您在本月初通过Google Spreadsheet设置的预算。如果只希望此标签在具有该标签名称的广告系列上运行,则也可以填写LABEL值。将其保留为空白可在所有广告系列上使用。

谢谢,
拉斯

/********************************
* 动态调整广告系列预算 v2.1
* Changelog v2.1 - Fixed opening of 电子表格
* Created By: 拉斯 Savage
* FreeAdWordsScripts.com
********************************/
// Let's set some constants
var TIMEFRAME = "THIS_MONTH";
//if the 运动 is not in the 电子表格, the 预算 is reset
//to this value 在 the beginning of the month.
var DEFAULT_BUDGET = 100; 
var SPREADSHEET_URL = "PLACE EMPYT SPREADSHEET URL HERE";
var LABEL = ""; //Fill in if you only want to operate on 运动s with this 标签
 
var SIG_FIGS = 1000; //this 意思s round all calculations to 3 decimal places
var MONTHLY_BUDGET = 0; // we will set this later
 
function main() {
  MONTHLY_BUDGET = _pull_budget_data_from_spreadsheet();
  var tot_cost_mtd = _get_total_cost();
  var is_first_of_the_month = ((new Date()).getDate() == 1);
  is_first_of_the_month = (is_first_of_the_month && ((new Date()).getHours() == 0));
  Logger.log("Total cost: " + tot_cost_mtd + ", Monthly 预算:" + MONTHLY_BUDGET);
   
  if(is_first_of_the_month) {
    _reset_budgets();
  } else {
    _adjust_campaign_budget(tot_cost_mtd);
  }
   
}
 
// Returns the total cost for the set TIMEFRAME
function _get_total_cost() {
  var camp_iter = (LABEL == "") ? AdWordsApp.campaigns().get() :
                                  AdWordsApp.campaigns()
                                    .withCondition("LabelNames CONTAINS_ANY ['"+LABEL+"']")
                                    .get();
   
  var tot_cost = 0;
  while(camp_iter.hasNext()) {
    tot_cost += camp_iter.next().getStatsFor(TIMEFRAME).getCost();
  }
  return tot_cost;
}
 
// Calculates 运行速度 and 广告 justs 运动 竞标 as needed.
function _adjust_campaign_budget(my_tot_cost) {
  var today = new Date();
  // Accounting for December
  var eom = (today.getMonth() == 11) ? new Date(today.getFullYear()+1,0,1) : 
                                       new Date(today.getFullYear(),today.getMonth()+1,1);
  var days_left = Math.round((eom-today)/1000/60/60/24);
  var days_spent = today.getDate();
  var run_rate = Math.round(my_tot_cost/days_spent*SIG_FIGS)/SIG_FIGS;
  var projected_total = my_tot_cost + (run_rate * days_left);
  var perc_over = Math.round(((MONTHLY_BUDGET-projected_total)/projected_total)*SIG_FIGS)/SIG_FIGS; 
  _change_spend(perc_over,my_tot_cost);
}
 
//Adjusts the 预算 for a given 运动 based on percentage of total spend
//Note: if the cost of a 运动 is $0 mtd, the 预算 is not changed.
function _change_spend(perc_to_change,my_tot_cost) {
  var camp_iter = (LABEL == '') ? AdWordsApp.campaigns()
                                    .withCondition("Status = ENABLED")
                                    .get() :
                                  AdWordsApp.campaigns()
                                    .withCondition("Status = ENABLED")
                                    .withCondition("LabelNames CONTAINS_ANY ['"+LABEL+"']")
                                    .get();
   
  while(camp_iter.hasNext()) {
    var camp = camp_iter.next();
    var camp_cost = camp.getStatsFor(TIMEFRAME).getCost();
    var perc_of_total = Math.round(camp_cost/my_tot_cost*SIG_FIGS)/SIG_FIGS;
    //If there is no cost for the 运动, let's not change it.
    var to_change = (perc_of_total) ? (perc_of_total*perc_to_change) : 0;
    camp.setBudget(camp.getBudget()*(1+to_change));
  }
}
 
// Resets the 预算 unevenly
function _reset_budgets() {
  var camp_budget_map = _pull_campaign_data_from_spreadsheet();
  var camp_iter = (LABEL == '') ? AdWordsApp.campaigns()
                                    .withCondition("Status = ENABLED")
                                    .get() :
                                  AdWordsApp.campaigns()
                                    .withCondition("Status = ENABLED")
                                    .withCondition("LabelNames CONTAINS_ANY ['"+LABEL+"']")
                                    .get();
  while(camp_iter.hasNext()) {
    var camp = camp_iter.next();
    if(camp_budget_map[camp.getName()]) {
      camp.setBudget(camp_budget_map[camp.getName()]/30.5);
    } else {
      camp.setBudget(DEFAULT_BUDGET);
    }
  }
}
 
function _pull_campaign_data_from_spreadsheet() {
  var 电子表格 = getSpreadsheet(SPREADSHEET_URL);
  var sheet = 电子表格.getActiveSheet();
  var data = sheet.getRange("A:B").getValues();
  if(data[0][0] == "") {
    //This 意思s this is the first run and we should populate the data.
    _populate_spreadsheet(sheet);
    data = sheet.getRange("A:B").getValues();
  }
  var 运动_budget_map = {};
  for(var i in data) {
    if(i == 0) { continue; } //ignore the header
    if(data[i][0] == "") { break; } //stop when there is no more data
    campaign_budget_map[data[i][0]] = parseFloat(data[i][1]);
  }
  return 运动_budget_map;
}
 
function _pull_budget_data_from_spreadsheet() {
  var 电子表格 = getSpreadsheet(SPREADSHEET_URL);
  var sheet = 电子表格.getActiveSheet();
  var data = sheet.getRange("A:B").getValues();
  if(data[0][0] == "") {
    //This 意思s this is the first run and we should populate the data.
    _populate_spreadsheet(sheet);
    data = sheet.getRange("A:B").getValues();
  }
  var tot_budget = 0;
  for(var i in data) {
    if(i == 0) { continue; } //ignore the header
    if(data[i][1] == "") { break; } //stop when there is no more data
    tot_budget += parseFloat(data[i][1]);
  }
  return tot_budget;
}
 
function _populate_spreadsheet(sheet) {
  sheet.clear();
  sheet.appendRow(['Campaign Name','Monthly Budget']);
  var camp_iter = (LABEL == '') ? AdWordsApp.campaigns()
                                    .withCondition("Status = ENABLED")
                                    .get() :
                                  AdWordsApp.campaigns()
                                    .withCondition("Status = ENABLED")
                                    .withCondition("LabelNames CONTAINS_ANY ['"+LABEL+"']")
                                    .get();
  while(camp_iter.hasNext()) {
    var camp = camp_iter.next();
    sheet.appendRow([camp.getName(),(camp.getBudget()*30.5)]);
  }
}
 
function getSpreadsheet(spreadsheetUrl) {
  return SpreadsheetApp.openByUrl(spreadsheetUrl);
}

2013年3月18日,星期一

动态调整广告系列预算

UPDATE 2013-04-05:此辽宁福利彩票中心有更新版本。看看这个。 动态调整广告系列预算v2.0.

更新2013-04-07:非常感谢FoxSUP帮助我跟踪更新预算的问题。修复了第78行,将当前预算乘以1 + to_change,而不仅仅是to_change。还修复了计算更改的错误(第76行)。

这是读者的请求:
我管理着许多小型企业PPC帐户,其中一些帐户有多个广告系列,并且它们通常具有相对较小的每月点击预算。我正在寻找一种方法来暂停所有广告系列,前提是整个帐户自一个月至今已花费了一定金额。

实际上,这是一个非常容易组合的辽宁福利彩票中心。下面是一个辽宁福利彩票中心,将执行此操作。您可以在辽宁福利彩票中心的开头设置MONTHLY_BUDGET,并每天在您的帐户上运行此辽宁福利彩票中心。一旦这些广告系列的总费用高于指定的预算,它将暂停该帐户中的所有广告系列。

然后,在下个月的第一天,它将再次启用这些广告系列。如果您未对以下辽宁福利彩票中心进行任何更改,则只需执行此操作。但是,让我们更进一步。

实际上,您可以使用辽宁福利彩票中心来获取和设置广告系列预算。假设您的每月预算是$ 100,但是您要确保您的广告在整个月中的投放量都没有变化。我在下面添加了一个名为_adjust_campaign_budget()的函数,可以通过辽宁福利彩票中心顶部的标志ADJUST_BUDGETS启用该函数。

然后,辽宁福利彩票中心将尝试计算广告系列的运行率,以确定您是否要满足预算。如果您要结束,它将降低每个广告系列的预算(按广告系列费用加权),从而使您实现目标。如果您的支出不足,它还会尝试增加您的广告系列预算,以使您达到目标。我还添加了一个_reset_budgets()函数,该函数在每月的第一天被调用。如果您更频繁地运行此辽宁福利彩票中心,则应启用代码以检查是否是该月第一天的第一个小时。

现在可以理解,此辽宁福利彩票中心带有一些注意事项。 该辽宁福利彩票中心可能使您花费很多钱。 我确信我正在测试的广告系列比您的广告系列要小得多,而且预算可以匹配。

谢谢,
拉斯

/******************************************
* Keep Your Campaigns In Budget
* Version 1.1
* ChangeLog v1.1 
*   - cleaned up code
*   - 广告 ded ability for any 日期
* Created By: 拉斯 Savage
* FreeAdWordsScripts.com
******************************************/
// Let's set some constants
var MONTHLY_BUDGET = 5000.00;

//If you want to work with a monthly 预算, leave START_DATE and END_DATE blank.
var TIMEFRAME = "THIS_MONTH";
//But if you want to work with a specific timeframe, fill these in.
//Use the format yyyyMMdd, so for Jan 12th, 2014, you would put 20140112.
var START_DATE = '';
var END_DATE = '';
 
//Set this to true if you want to 调整预算 or
//keep set to false if you want to just 暂停 all the 运动s
//when you hit your 预算
var ADJUST_BUDGETS = false;
var DECIMAL_PLACES = 3;
 
function main() {
  var totalCostMTD = getTotalCost();
  var isFirstOfTheMonth = ((new Date()).getDate() == 1);
  if(START_DATE && END_DATE) {
    var today = new Date();
    today.setHours(0,0,0,0);
    var startDate = new Date(START_DATE.substring(0,4),
                             parseFloat(START_DATE.substring(4,6))-1,
                             START_DATE.substring(6,8));
    isFirstOfTheMonth = (startDate.getTime() == today.getTime());
  }
  //if you run this script more than once per day, uncomment the next line
  //isFirstOfTheMonth = (isFirstOfTheMonth && ((new Date()).getHour() == 0));
  Logger.log("Total cost: " + totalCostMTD + 
           ", Monthly 预算:" + MONTHLY_BUDGET +
           ", isFirstOfTheMonth: "+isFirstOfTheMonth);
   
  if(ADJUST_BUDGETS) {
    if(isFirstOfTheMonth) {
      resetBudgets();
    } else {
      adjustCampaignBudget(totalCostMTD);
    }
  } else {
    if(totalCostMTD >= MONTHLY_BUDGET) {
      //If we have hit the limit, 暂停 all 广告
      enableOrDisableCampaigns(true);
    } else {
      // let's check if it's the first day of the month
      if((new Date()).getDate() == 1) {
        //enable all the 运动s
        enableOrDisableCampaigns(false);
      }
    }
  }
}
 
// Returns the total cost for the set TIMEFRAME
function getTotalCost() {
  var campIter = AdWordsApp.campaigns().get();
   
  var totalCost = 0;
  while(campIter.hasNext()) {
    if(START_DATE && END_DATE) {
      totalCost += campIter.next().getStatsFor(START_DATE,END_DATE).getCost();
    } else {
      totalCost += campIter.next().getStatsFor(TIMEFRAME).getCost();
    }
  }
  return totalCost;
}
 
// Enables or Disables All Campaigns In Account
function enableOrDisableCampaigns(shouldDisable) {
  var campIter = AdWordsApp.campaigns().get();
  while(campIter.hasNext()) { 
    if(shouldDisable) { 
      campIter.next().pause(); 
    } else { 
      campIter.next().enable(); 
    }
  }
}
 
// Calculates 运行速度 and 广告 justs 运动 竞标 as needed.
function 广告 justCampaignBudget(myTotalCost) {
  var today = new Date();
  // Accounting for December
  var eom;
  if(START_DATE && END_DATE) {
    eom = new Date(END_DATE.substring(0,4),
                   parseFloat(END_DATE.substring(4,2))-1,
                   END_DATE.substring(6,2));
  } else {
    eom = (today.getMonth() == 11) ? new Date(today.getFullYear()+1,0,1) : 
                                     new Date(today.getFullYear(),today.getMonth()+1,1);
  }
  var daysLeft = Math.round((eom-today)/1000/60/60/24);
  var daysSpent;
  if(START_DATE && END_DATE) {
    var startDate = new Date(START_DATE.substring(0,4),
                             parseFloat(START_DATE.substring(4,2))-1,
                             START_DATE.substring(6,2));
    daysSpent = Math.round((today-startDate)/1000/60/60/24);
  } else {
    daysSpent = today.getDate();
  }
  var runRate = round(myTotalCost/daysSpent);
  var projectedTotal = myTotalCost + (runRate * daysLeft);
  var percOver = round((MONTHLY_BUDGET-projectedTotal)/projectedTotal);
   
  changeSpend(percOver,myTotalCost);
}
 
//Adjusts the 预算 for a given 运动 based on percentage of total spend
//Note: if the cost of a 运动 is $0 mtd, the 预算 is not changed.
function changeSpend(percToChange,myTotalCost) {
  var campIter = AdWordsApp.campaigns().withCondition("Status = ENABLED").get();
   
  while(campIter.hasNext()) {
    var camp = campIter.next();
    var campCost = (START_DATE && END_DATE) ? camp.getStatsFor(START_DATE,END_DATE).getCost()
                                            : camp.getStatsFor(TIMEFRAME).getCost();
    var percOfTotal = round(campCost/myTotalCost);
    //If there is no cost for the 运动, let's not change it.
    var toChange = (percOfTotal) ? (percOfTotal*percToChange) : 0;
    camp.setBudget(camp.getBudget()*(1+toChange));
  }
}
 
// Resets the 预算 evenly across all 运动s
function resetBudgets() {
  Logger.log('Resetting 预算s 在 the first of the period.');
  var campIter = AdWordsApp.campaigns().withCondition("Status = ENABLED").get();
  var campCount = 0;
  while(campIter.hasNext()) {
    campCount++;
    campIter.next();
  }
  campIter = AdWordsApp.campaigns().withCondition("Status = ENABLED").get();
  while(campIter.hasNext()) {
    campIter.next().setBudget(MONTHLY_BUDGET/campCount);
  }
}

// A helper function to make rounding a little easier
function round(value) {
  var decimals = Math.pow(10,DECIMAL_PLACES);
  return Math.round(value*decimals)/decimals;
}

2013年2月26日,星期二

使用AdWords辽宁福利彩票中心对OAuth服务进行身份验证

如今,许多API使用OAuth来使用户进行身份验证并调用其服务。 一个非常受欢迎的例子是 Twitter REST API.  浏览AdWords辽宁福利彩票中心参考,您会发现,使用AdWords辽宁福利彩票中心可以做的一件很酷的事情是使用UrlFetchApp从URL提取数据。

因此,我问的问题是“我可以编写辽宁福利彩票中心以使用OAuth进行身份验证并从Twitter REST API检索数据吗?”

显然,我看过的第一个地方是 OAuthConfig UrlFetchApp的类。 我设置了配置并尝试拨打电话,但始终收到验证错误。 一项小小的研究表明,我并不是唯一遇到此问题的人。 问题源于以下事实:通常,用户需要先通过某种对话框进行身份验证,然后才能访问数据。看到这个例子关于 连接到Picasa网络相册 更多细节。

但是后来我发现 一个帖子 有关某人设法通过在其辽宁福利彩票中心中重新实现OAuth身份验证系统并使用标准UrlFetchApp.fetch()请求来解决此问题的信息。

我正面临挑战,所以我自己重新创建了它。 原来是很多代码,但是最后,您真正需要担心的唯一事情是从Twitter获取正确的密钥并调用_build_authorization_string()。

谢谢,
拉斯

//-----------------------------------
// Authenticate and Connect to OAuth Service
// Created By: 拉斯 Savage
// FreeAdWordsScripts.com
//-----------------------------------
function main() {
  //Define the Twitter Keys and Secrets
  //More info on obtaining these can be found 在 //dev.twitter.com/docs/auth/tokens-devtwittercom
  var 认证_key_stuff = {
    "consumer_key" : "your consumer key",
    "consumer_secret" : "your consumer secret",
    "access_token" : "your access token",
    "access_token_secret" : "your access token secret"
  };

  // Update this with the REST url you want to 呼叫.  I only tested it with GET
  // but i don't think there is anything stopping a POST request from working.
  var url_stuff = {  
    "http_method" : 'GET',
    "base_url" : "//api.twitter.com/1.1/statuses/user_timeline.json"
  };

  //Add the parameters for the REST url you want to 呼叫.
  var url_param_stuff = {
    "screen_name" : "russellsavage" //hey that's me!
  };
  
  // Don't touch this stuff
  var other_oauth_data = {
    "认证_nonce" : Utilities.base64Encode(Math.random() +
          "secret_sauce" +
          (new Date()).getTime()).replace(/(?!\w)/g, ''),
    "认证_signature_method" : "HMAC-SHA1",
    "认证_timestamp" : Math.round((new Date()).getTime() / 1000.0),
    "认证_version" : "1.0"
  };
  
  // Here is where the magic happens
  var auth_string = _build_authorization_string(oauth_key_stuff,url_stuff,url_param_stuff,other_oauth_data);

  var options = {
    "headers" : { "Authorization" :  auth_string }
  };
    
  var url = _build_url(url_stuff,url_param_stuff);
  var response = UrlFetchApp.fetch(url, options);
  var tweets = JSON.parse(response.getContentText());
  
  //now let's log my amazing tweets!
  for(var tweet in tweets) {
    var t = tweets[tweet];
    Logger.log(t.text);
  }
 
  // HELPER FUNCTIONS BELOW
  
  function _build_url(base_url,param_stuff){
    var url = base_url.base_url;
    if(param_stuff != {}) {
      url += '?';
    }
    for(var key in param_stuff) {
      url += key + "=";
      url += encodeURIComponent(param_stuff[key]);
      url += '&';
    }
    return url.slice(0,-1);
  }
  
  function _build_param_string(auth_keys,url_data,oauth_data) {
    var data_for_param_string = {
      "认证_consumer_key" : auth_keys.consumer_key,
      "认证_nonce" : 认证_data.oauth_nonce,
      "认证_signature_method" : 认证_data.oauth_signature_method,
      "认证_timestamp" : 认证_data.oauth_timestamp,
      "认证_token" : auth_keys.access_token,
      "认证_version" : 认证_data.oauth_version
    };
    
    // 广告 d 广告 ditional url values
    for(var my_key in url_data) { 
      data_for_param_string[my_key] = url_data[my_key]; 
    }
    
    // find and sort the keys for later
    var keys = [];
    for(var key in data_for_param_string) {
      keys.push(key);
    }
    keys.sort();
    
    //finally build and return the param string
    var param_string = "";
    for(var i in keys) {
      param_string += keys[i] + "=" + encodeURIComponent(data_for_param_string[keys[i]]);
      if(i < keys.length - 1) {
        param_string += "&";
      }
    }
    
    return param_string;
  }
  
  function _build_sig_base_string(my_url_stuff,my_param_string) {
    return my_url_stuff.http_method +
      "&" + encodeURIComponent(my_url_stuff.base_url) +
      "&" + encodeURIComponent(my_param_string);
  }
  
  function _build_sigining_key(my_key_stuff) {
    return encodeURIComponent(my_key_stuff.consumer_secret) + 
      "&" + encodeURIComponent(my_key_stuff.access_token_secret);
  }
  
  function _build_oauth_signature(base_string,sign) {
    return Utilities.base64Encode(
      Utilities.computeHmacSignature(
        Utilities.MacAlgorithm.HMAC_SHA_1, 
        base_string, 
        sign
      )
    );
  }
  
  function _build_authorization_string(my_key_stuff,my_url_stuff,my_url_param_stuff,my_oauth_stuff) {
    var param_string = _build_param_string(my_key_stuff,my_url_param_stuff,my_oauth_stuff);
    var sig_base_string = _build_sig_base_string(my_url_stuff,param_string);
    var signing_key = _build_sigining_key(my_key_stuff);
    var 认证_signature = _build_oauth_signature(sig_base_string,signing_key);
    return "OAuth " +
           encodeURIComponent("认证_consumer_key") + '="' + 
             encodeURIComponent(my_key_stuff.consumer_key) + '", ' +
           encodeURIComponent("认证_nonce") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_nonce) + '", ' +
           encodeURIComponent("认证_signature") + '="' + 
             encodeURIComponent(oauth_signature) + '", ' +
           encodeURIComponent("认证_signature_method") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_signature_method) + '", ' +
           encodeURIComponent("认证_timestamp") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_timestamp) + '", ' +
           encodeURIComponent("认证_token") + '="' + 
             encodeURIComponent(my_key_stuff.access_token) + '", ' +
           encodeURIComponent("认证_version") + '="' + 
             encodeURIComponent(my_oauth_stuff.oauth_version) + '"';
    
  }
  
}