Trying to call a GAS function with Python from Cloud Shell Editor, but not sure what I'm doing wrong. I scoured through stackoverflow, and tried using executable API, but that forced me to use OAuth2.0 which I could never get it to work (tells me to authorize through a link then it 403's me). The function below at least lists the functions that I have on GAS, but the trying to run the function returns a 404.
from __future__ import print_function
# bot.py
import os
import pickle
import os.path
import google.auth
from google.oauth2 import service_account
from googleapiclient.discovery import build
# Replace 'your-service-account.json' with the path to your service account JSON file
service_account_json = 'service.json'
# Replace with your Google Apps Script project ID
script_id = 'UNIQUE-SCRIPTID'
# Define the scopes required for the Google Apps Script API
SCOPES = [
'https://www.googleapis.com/auth/script.projects',
'https://www.googleapis.com/auth/script.scriptapp'
]
def authorize_script_api(service_account_json):
# Load the service account credentials from the JSON file
creds = google.oauth2.service_account.Credentials.from_service_account_file(
service_account_json, scopes=SCOPES)
return creds
def list_functions(script_id, credentials):
try:
# Build the Google Apps Script API service
service = build('script', 'v1', credentials=credentials)
# Get the content of the specified Google Apps Script project
content = service.projects().getContent(scriptId=script_id).execute()
if 'files' in content:
for file in content['files']:
if 'functionSet' in file:
function_set = file['functionSet']
if 'values' in function_set:
for func in function_set['values']:
if 'name' in func:
print(func['name'])
except Exception as e:
print(f"An error occurred: {e}")
def run_function(script_id, credentials, function_name):
try:
# Build the Google Apps Script API service
service = build('script', 'v1', credentials=credentials)
# Create a request to run the specified function
request = {
"function": function_name
}
# Execute the function in the Google Apps Script project
response = service.scripts().run(scriptId=script_id, body=request).execute()
print(response)
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == '__main__':
# Authorize the script API
credentials = authorize_script_api(service_account_json)
# List functions in the Google Apps Script project
list_functions(script_id, credentials)
# Specify the name of the function you want to run
function_name = "helloworld"
# Run the specified function in the Google Apps Script project
run_function(script_id, credentials, function_name)
returns the following:
helloworld
setTrigger
scheduledTrigger
deleteTriggers
An error occurred: <HttpError 404 when requesting https://script.googleapis.com/v1/scripts/UNIQUE-SCRIPTID:run?alt=json returned "Requested entity was not found.". Details: "Requested entity was not found.">
This is what content returns:
{'scriptId': 'UNIQUE-SCRIPTID', 'files': [{'name': 'appsscript', 'type': 'JSON', 'source': '{
"timeZone": "America/Los_Angeles",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"executionApi": {
"access": "MYSELF"
},
"oauthScopes": [
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/script.scriptapp"
]
}', 'lastModifyUser': {'photoUrl': 'redacted'}, 'createTime': '2022-09-06T01:19:20.404Z', 'updateTime': '2023-10-09T07:08:55.871Z', 'functionSet': {}}, {'name': 'Code', 'type': 'SERVER_JS', 'source': 'function helloworld() {
console.log("hello!");
}
function setTrigger() {
deleteTriggers();
var times = [[19,15]]; // 7:15PM
times.forEach(t_el => scheduledTrigger(t_el[0],t_el[1]));
}
function scheduledTrigger(hours,minutes) {
var today_D = new Date();
var year = today_D.getFullYear();
var month = today_D.getMonth();
var day = today_D.getDate();
pars = [year,month,day,hours,minutes];
var scheduled_D = new Date(...pars);
var hours_remain=Math.abs(scheduled_D - today_D) / 36e5;
ScriptApp.newTrigger("Announcement")
.timeBased()
.after(hours_remain * 60 *60 * 1000)
.create()
}
function deleteTriggers() {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if ( triggers[i].getHandlerFunction() == "Announcement") {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
}', 'lastModifyUser': {'photoUrl': 'redacted'}, 'createTime': '2022-09-06T01:19:20.397Z', 'updateTime': '2023-10-09T07:08:55.871Z', 'functionSet': {'values': [{'name': 'helloworld'}, {'name': 'setTrigger'}, {'name': 'scheduledTrigger', 'parameters': ['hours', 'minutes']}, {'name': 'deleteTriggers'}]}}]}
Issue and workaround
When I saw your showing script, I was worried that the reason for your current issue might be due to
Warning: The Apps Script API does not work with service accounts.. Ref And, when I tested your situation using the service account, in the current stage, it seems that the script can be retrieved using the service account. However, it seems that the script cannot be run using the service account, and I confirmed the same error message with you. I'm worried that this might be the reason for your current issue.In order to achieve your goal, in that case, as a workaround, how about executing your Google Apps Script using Web Apps? I think that when Web Apps is used, the Google Apps Script can be run with the service account. The flow of this workaround is as follows.
1. Share your Google Apps Script with a service account
Please share your Google Apps Script with the email of the service account. If you have already done this, it is not required to run this.
2. Modify Google Apps Script
Please add the following function to your Google Apps Script.
3. Deploy Web Apps
The detailed information can be seen in the official document.
Please set this using the new IDE of the script editor.
https://script.google.com/macros/s/###/exec. This URL is used with Python script.4. Modify Python script
Please set your Web Apps URL to
url = "https://script.google.com/macros/s/###/exec" # Please set your Web Apps URL..Testing:
When I tested this modified script, I could retrieve the function names from
list_functions. And also, I could retrieve the response value fromrun_function.Note:
When you modify the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the details of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
References: