How to insert Google Event with Extended Properties in GS? - google-apps-script

I want to add / insert new event with extened properties in Google Script = .gs file.
I found code example for Calendar API - Events insert - see below. But the code uses the JavaScript client library. I want the code to run from GS file. I tried to modify but it did not work.
When using the code I want to be able to specify any calendar. Not only "primary".
// Refer to the JavaScript quickstart on how to setup the environment:
// https://developers.google.com/calendar/quickstart/js
// Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any
// stored credentials.
var event = {
'summary': 'Google I/O 2015',
'location': '800 Howard St., San Francisco, CA 94103',
'description': 'A chance to hear more about Google\'s developer products.',
'start': {
'dateTime': '2015-05-28T09:00:00-07:00',
'timeZone': 'America/Los_Angeles'
},
'end': {
'dateTime': '2015-05-28T17:00:00-07:00',
'timeZone': 'America/Los_Angeles'
},
'recurrence': [
'RRULE:FREQ=DAILY;COUNT=2'
],
'attendees': [
{'email': 'lpage#example.com'},
{'email': 'sbrin#example.com'}
],
'reminders': {
'useDefault': false,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10}
]
}
};
var request = gapi.client.calendar.events.insert({
'calendarId': 'primary',
'resource': event
});
request.execute(function(event) {
appendPre('Event created: ' + event.htmlLink);
});
Could someone please explain the difference between private and shared extended properties?
I am able to create new event using below code but looks like it will not store extended properties.
function getCalendar() {
var calendarId = 'processor#mydomain.com'
var calendar = CalendarApp.getCalendarById(calendarId)
Logger.log('The calendar is named "%s".', calendar.getName());
var eventOption = {
location: 'The Moon',
description: 'link na akci je https://us02web.zoom.us/j/83314336043',
extendedProperties: { // Extended properties of the event.
private: { // Properties that are private to the copy of the event that appears on this calendar.
creator: "Radek", // The name of the private property and the corresponding value.
},
}
}
var event = calendar.createEvent('test event from the script',
new Date(),
new Date(),
eventOption
);
var eventId = event.getId().replace(/#.*/,'') // // Remove #google.com from eventId
Logger.log('Event ID: ' + eventId)
calendarId = 'primary'
var eventSaved = Calendar.Events.get(encodeURIComponent(calendarId), eventId)
var testEx = event.extendedProperties
var test = event.extendedProperties.private["creator"];
}

Answer for question 1:
Could someone please explain the difference between private and shared extended properties?
The official document says as follows.
extendedProperties.private: Properties that are private to the copy of the event that appears on this calendar.
extendedProperties.shared: Properties that are shared between copies of the event on other attendees' calendars.
For example, when a new event is created with the values of extendedProperties.private and extendedProperties.shared by including the attendees, you can see both values. But, the attendees can see only the value of extendedProperties.shared.
Is this explanation useful?
Answer for question 2:
I want to add / insert new event with extened properties in Google Script = .gs file.
When I saw the official document of the method of createEvent(title, startTime, endTime, options), it seems that options has no property of extendedProperties. Ref I thought that this is the reason for your issue. If you want to create a new event including the values of extendedProperties.private and extendedProperties.shared, how about using Calendar API of Advanced Google services?
The sample script is as follows.
const calendarId = "###"; // Please set your calendar ID.
// Create a new event including extendedProperties.
const params = {
start: { dateTime: "2022-04-27T00:00:00Z" },
end: { dateTime: "2022-04-27T01:00:00Z" },
extendedProperties: {
private: { key1: "value1" },
shared: { key2: "value2" }
},
summary: "sample",
attendees: [{ email: "###" }] // Please set the email of attendee, if you want to include.
};
const res1 = Calendar.Events.insert(params, calendarId);
// Check the value of extendedProperties
const res2 = Calendar.Events.get(calendarId, res1.id);
console.log(res2.extendedProperties)
When this script is run by the owner of the calendar, you can see both values of extendedProperties.private and extendedProperties.shared.
When you get this event by the attendee, you can see only the value of extendedProperties.shared.
References:
createEvent(title, startTime, endTime, options)
Events: insert

Could someone please explain the difference between private and shared extended properties?
Based on documentation, shared extended properties are visible and editable by attendees while private set on one attendee's local "copy" of the event.
To add extended properties to events with Apps Script, you can do it with the advanced Calendar service. For this, you need to add the “Google Calendar API” service in your Apps Script project, on the left side of the screen, click on the “+” next to “Services”, search for “Google Calendar API”, click on it and click “Add”.
After completing the steps mentioned above, you can test this script I created as an example.
function createEvent() {
var calendarId = 'processor#mydomain.com' //you can specify the calendar with the calendar id
var start = new Date();
var end = new Date();
var event = {
"location": "The Moon",
"description": "link na akci je https://us02web.zoom.us/j/83314336043",
"start": {
"dateTime": start.toISOString(),
},
"end": {
"dateTime": end.toISOString()
},
"extendedProperties": {
"private": {
"creator": "Radek"
}
}
};
event = Calendar.Events.insert(event, calendarId);
Logger.log('Event ID: ' + event.id);
}

Related

"Invalid number of arguments" error when trying to update an event

I am trying to update calendar events from google apps script. I have the calendar ID, event ID, and objects I am trying to update as a variable:
var eventinfo = {
"calendarId": calID
"eventId": eventID,
"resource": {
"description": "1234"
}
};
//update the event description and location
var updater;
try {
updater = Calendar.Events.update(eventinfo);
Logger.log('Successfully updated event: ' + i);
} catch (e) {
Logger.log('Fetch threw an exception: ' + e);
}
I am getting this error:
Fetch threw an exception: Exception: Invalid number of arguments provided. Expected 3-5 only
Previously, I had attempted to invoke the update method this way .update(calID, eventID, eventinfo) where event info was an object with just a description, but that was returning the error saying bad call.
I think I am missing something in my object argument.
Issues:
First of all, you forgot a comma in the definition of eventinfo
between the first and the second row.
However, I don't think your approach will work, because you don't
pass an event object in the Calendar.Events.update() function. The structure should be like that:
Calendar.Events.update(
event,
calendarId,
event.id
);
Solution/Example:
The following example updates the very first event in the future. In
particular, it updates the title (summary), description and place but
feel free modify that if you want:
function updateNextEvent() {
const calendarId = 'primary';
const now = new Date();
const events = Calendar.Events.list(calendarId, {
timeMin: now.toISOString(),
singleEvents: true,
orderBy: 'startTime',
maxResults: 1
});
var event = events.items[0]; //use your own event object here if you want
event.location = 'The Coffee Shop';
event.description = '1234';
event.summary = 'New event';
event = Calendar.Events.update(
event,
calendarId,
event.id
);
}
Of course, don't forget to switch on the Calendar API from Resources => Advanced Google services.
References:
Advanced Calendar Service

My GSM Gmail addon does not show the card

I have built a GSM add on and published it for my domain. I built the code, on Google Apps Script and set it up in Google API Console. I installed it for my domain, but it does not show the card in Gmail It should show in the sidebar, and in the compose window. It works fine when I install the head version from within google apps script, but then I publish it to GSM for users in my organization it doesn't work. The purpose of the addon is to collect information from fields in a card and use that in an email template. I think it might be something to do with OAuth scopes but I am not sure. This is my very first GSM project and I don't know what OAuth scopes I should declare in my code and in the Google API console.
Here is my code, I have 2 files, appscript.json, and code.gs.
code.gs:
function onGmailCompose(e) {
console.log(e);
var header = CardService.newCardHeader()
.setTitle('Use Template')
.setSubtitle('Use the template for sending an email after a review has been published.');
// Create text input for entering the cat's message.
var input2 = CardService.newTextInput()
.setFieldName('FName')
.setTitle('First Name')
.setHint('What is the readers first name?');
var input3 = CardService.newTextInput()
.setFieldName('BookTitle')
.setTitle('Reviewed Book Title')
.setHint('What is the title of the book reviewed?');
var input4 = CardService.newTextInput()
.setFieldName('BookAuthor')
.setTitle('Reviewed Book Author')
.setHint('Who is the author of the book reviewed?');
// Create a button that inserts the cat image when pressed.
var action = CardService.newAction()
.setFunctionName('useTemplate');
var button = CardService.newTextButton()
.setText('Use Template')
.setOnClickAction(action)
.setTextButtonStyle(CardService.TextButtonStyle.FILLED);
var buttonSet = CardService.newButtonSet()
.addButton(button);
// Assemble the widgets and return the card.
var section = CardService.newCardSection()
.addWidget(input2)
.addWidget(input3)
.addWidget(input4)
.addWidget(buttonSet);
var card = CardService.newCardBuilder()
.setHeader(header)
.addSection(section);
return card.build();
}
function useTemplate(e) {
console.log(e);
var FName = e.formInput.FName;
var Title = e.formInput.BookTitle;
var Author = e.formInput.BookAuthor;
var now = new Date();
var htmlIntro = '<p>Hello, ';
var html2 = ' Thank you for writing a book review at Good Book Reviews on ';
var html3 = ' by ';
var html4 = '. You Review has been published to our site. Any personal information you included was NOT published, including first name, last name, age, and email address. Only info you wrote about the book was published. You can see it right here! If you need anything else, feel free to contact us at support#goodbookreviews.page or reply to this email to contact us. <br> Happy Reading,<br> The Book Review Team</p>';
var message = htmlIntro + FName + html2 + Title + html3 + Author + html4;
var response = CardService.newUpdateDraftActionResponseBuilder()
.setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()
.addUpdateContent(message, CardService.ContentType.MUTABLE_HTML)
.setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT))
.build();
return response;
}
function onGmailMessage(e) {
console.log(e);
var header = CardService.newCardHeader()
.setTitle('Unavailable')
.setSubtitle('Open the compose window to use template');
var card = CardService.newCardBuilder()
.setHeader(header);
return card.build();
}
appscript.json:
{
"timeZone": "America/Chicago",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": ["https://www.googleapis.com/auth/gmail.compose"],
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "Review Published Email Template",
"logoUrl": "https://goodbookreviews.page/Logo.png",
"useLocaleFromApp": true,
"universalActions": [{
"label": "Book Review ",
"openLink": "https://www.goodbookreviews.page"
}]
},
"gmail": {
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "onGmailMessage"
}],
"composeTrigger": {
"selectActions": [{
"text": "Use Template",
"runFunction": "onGmailCompose"
}],
"draftAccess": "NONE"
}
}
}
}
The scopes that I specify in the OAuth consent screen page are here:
email
profile
openid
https://www.googleapis.com/auth/gmail.compose
The Email, profile, and openid are added by default and they are mandatory
Here are the scopes that I specify in the configuration page of the GSM SDK.
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/gmail.compose
You are very close to making this add-on works. You only need to add some scopes to your manifest file. Your final manifest should look similar to this one:
{
"timeZone":"America/Chicago",
"dependencies":{
},
"exceptionLogging":"STACKDRIVER",
"oauthScopes":[
"https://www.googleapis.com/auth/gmail.compose",
"https://www.googleapis.com/auth/gmail.addons.current.action.compose",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/script.locale"
],
"runtimeVersion":"V8",
"addOns":{
"common":{
"name":"Review Published Email Template",
"logoUrl":"https://goodbookreviews.page/Logo.png",
"useLocaleFromApp":true,
"universalActions":[
{
"label":"Book Review ",
"openLink":"https://www.goodbookreviews.page"
}
]
},
"gmail":{
"contextualTriggers":[
{
"unconditional":{
},
"onTriggerFunction":"onGmailMessage"
}
],
"composeTrigger":{
"selectActions":[
{
"text":"Use Template",
"runFunction":"onGmailCompose"
}
],
"draftAccess":"NONE"
}
}
}
}
The rest of your code is correct. I deployed your add-on with this manifest, and it worked. Don't hesitate to ask any additional doubt if you need further clarification.

ReferenceError: ConferenceDataService is not defined

I am trying to develop google calendar add-on like zoom meeting.
In appsscript.json file, below code is there.
"calendar": {
"conferenceSolution": [{
"onCreateFunction": "createConference",
"id": "1",
"name": "Meeting",
"logoUrl": "https://companyxyz.com/images/logo192.png"
}],
"eventOpenTrigger": {
"runFunction": "buildSimpleCard"
},
"currentEventAccess": "READ_WRITE"
}
}
In Calendar.gs, below code is there.
function createConference(e) {
Logger.log(e);
var dataBuilder = ConferenceDataService.newConferenceDataBuilder();
return dataBuilder.build();
}
/**
* Build a simple card with a button that sends a notification.
* This function is called as part of the eventOpenTrigger that builds
* a UI when the user opens a Calendar event.
*
* #param e The event object passed to eventOpenTrigger function.
* #return {Card}
*/
function buildSimpleCard() {
var buttonAction = CardService.newAction()
.setFunctionName('onSaveConferenceOptionsButtonClicked')
.setParameters(
{'phone': "1555123467", 'adminEmail': "joyce#example.com"});
var button = CardService.newTextButton()
.setText('Add new attendee')
.setOnClickAction(buttonAction);
var buttonSet = CardService.newButtonSet()
.addButton(button);
var section = CardService.newCardSection()
.setHeader("addon")
.addWidget(buttonSet);
var card = CardService.newCardBuilder()
.addSection(section)
//.setFixedFooter(footer);
return card.build();
// Check the event object to determine if the user can set
// conference data and disable the button if not.
// if (!e.calendar.capabilities.canSetConferenceData) {
// button.setDisabled(true);
// }
// ...continue creating card sections and widgets, then create a Card
// object to add them to. Return the built Card object.
}
/**
* Callback function for a button action. Sets conference data for the
* Calendar event being edited.
*
* #param {Object} e The action event object.
* #return {CalendarEventActionResponse}
*/
function onSaveConferenceOptionsButtonClicked(e) {
var parameters = e.commonEventObject.parameters;
// Create an entry point and a conference parameter.
var phoneEntryPoint = ConferenceDataService.newEntryPoint()
.setEntryPointType(ConferenceDataService.EntryPointType.PHONE)
.setUri('tel:' + parameters['phone']);
var adminEmailParameter = ConferenceDataService.newConferenceParameter()
.setKey('adminEmail')
.setValue(parameters['adminEmail']);
// Create a conference data object to set to this Calendar event.
var conferenceData = ConferenceDataService.newConferenceDataBuilder()
.addEntryPoint(phoneEntryPoint)
.addConferenceParameter(adminEmailParameter)
.setConferenceSolutionId(1)
.build();
return CardService.newCalendarEventActionResponseBuilder()
.setConferenceData(conferenceData)
.build();
}
I have published this add-on from Publish->Deploy from menifest.
Executing this code giving me error of ReferenceError: ConferenceDataService is not defined.
I have searched all the possible references, but not able to get any solution.
Please suggest me proper solution for this.
According to this comment from this issue here, it looks like there has been a change regarding this.
When testing the above code, the ReferenceError: ConferenceDataService is not defined. is not displayed anymore and the code runs as expected.
For other methods specific to the ConferenceDataService you can check the documentation here.
Reference
Apps Script ConferenceDataService.
Cannot create conference with 3rd party Conference Solution from conference solutions Dropdown in Google Calendar

Google Slides: newly inserted table not found

I´m wondering what is going on. I have two functions which both are working good when called one after one:
function createTable() {
var slidesPage = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI').getSlides()[0];
var table = slidesPage.insertTable(7, 4);
}
function changeColumnWidth() {
var slidesPage = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI').getSlides()[0];
var tableId = slidesPage.getTables()[0].getObjectId();
var requests = [{
updateTableColumnProperties: {
objectId: tableId,
"columnIndices": [ 1, 3],
"tableColumnProperties": {
"columnWidth": {
"magnitude": 80,
"unit": "PT"
}
},
"fields": "columnWidth"
}
}];
var createSlideResponse = Slides.Presentations.batchUpdate({
requests: requests
}, '1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI');
}
But trying to combine these two functions like:
function combined() {
createTable();
changeColumnWidth();
}
I´m getting Error:
Invalid requests[0].updateTableColumnProperties: The object (SLIDES_API456304911_0) could not be found.
Wondering if the insertTable method is asynchronous and therefore the created table is not ready?
Thanks for any help.
How about this modification? Please think of this as one of several workarounds. In my workaround, I used saveAndClose() for your situation. Using this, I thought to separate the process of SlidesApp and Slides API.
Modification points :
Save and close the slide using saveAndClose() after the table was inserted.
Return an object ID of inserted table to use at changeColumnWidth().
At changeColumnWidth(), the table is modified by Slides API using the received object ID.
Modified script :
function combined() {
var tableId = createTable(); // Modified
changeColumnWidth(tableId); // Modified
}
function createTable() {
var slide = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI'); // Modified
var slidesPage = slide.getSlides()[9]; // Modified
var table = slidesPage.insertTable(7, 4);
slide.saveAndClose(); // Added
return table.getObjectId();
}
function changeColumnWidth(tableId) { // Modified
// var slidesPage = SlidesApp.openById('1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI').getSlides()[0]; // This line is not used.
// var tableId = slidesPage.getTables()[0].getObjectId(); // This line is not used because slidesPage.getTables().length becomes 0.
var requests = [{
updateTableColumnProperties: {
objectId: tableId,
"columnIndices": [ 1, 3],
"tableColumnProperties": {
"columnWidth": {
"magnitude": 80,
"unit": "PT"
}
},
"fields": "columnWidth"
}
}];
var createSlideResponse = Slides.Presentations.batchUpdate({
requests: requests
}, '1QWRV4eQzGNNBz4SkR3WPurTL3O60oGYxQpBu63KrUoI');
}
Note :
For the slide which is saved and closed by saveAndClose(), when the slide is reopened, the inserted table cannot be retrieved. When the table is tried to be retrieved using getTables() again, the length becomes 0. But at Slides API, the object ID of table can be retrieved. So I thought that the issue might be able to be solved by returning the object ID of table after the table was inserted.
But I couldn't understand about the reason that the values retrieved by getTables() from the reopened Slide become "0" yet. I'm sorry.
Reference :
saveAndClose()
If this workaround was not what you want, I'm sorry.
To achieve your goal - create a table with a specified layout and specific column sizes in one function - you should use the Slides API for the entire task. The Slides API lets you both create and modify the same element in the same batch request, if you provided a unique object ID for it. Otherwise, you have to first create the element, then send the modification request using the objectId found in the response to the first request. This second approach is essentially the behavior you were experiencing when the function calls were done separately.
There are restrictions on user-supplied IDs, naturally:
objectId string: A user-supplied object ID.If you specify an ID, it must be unique among all pages and page elements in the presentation. The ID must start with an alphanumeric character or an underscore (matches regex [a-zA-Z0-9_] ); remaining characters may include those as well as a hyphen or colon (matches regex [a-zA-Z0-9_-:] ). The length of the ID must not be less than 5 or greater than 50.If you don't specify an ID, a unique one is generated.
Given that hyphens are allowed, we can use the Utilites.getUuid() method to help supply our own unique object IDs.
When mixing SlidesApp and Slides, it is very likely that internal Google optimizations (e.g. write-caching) change the operation order. By restricting to a single service for related task operations, we can ensure that the objects we need are available when needed.
This example uses two methods that make Request objects for batchUpdate and ultimately creates a presentation, adds a blank slide, adds a table and modifies it, and then creates another blank slide.
function makeCreateTableRequest_(slideId, rows, columns, shouldSupplyID) {
const tablerq = {
rows: rows,
columns: columns,
elementProperties: {
pageObjectId: slideId,
/** size: {
height: {...},
width: {...}
},
transform: { ... } */
}
};
// If asked to use a custom ID (e.g. also going to modify this table), use a unique one.
if (shouldSupplyID)
tablerq.objectId = ("table" + Utilities.getUuid()).slice(0, 50);
return {createTable: tablerq};
}
function makeModifyTableColumnPropsRequest_(tableId, newWidthDimension, indicesArray) {
const rq = {
objectId: tableId,
fields: "columnWidth" // There are no other fields for this request as of 2018-07
};
if (newWidthDimension && newWidthDimension.magnitude !== undefined && newWidthDimension.unit)
rq.tableColumnProperties = { columnWidth: newWidthDimension };
if (indicesArray && indicesArray.length)
rq.columnIndices = indicesArray;
return {updateTableColumnProperties: rq};
}
function createPresentation_() {
const newPres = { title: "API-created Presentation" };
// Presentations are huge... limit the metadata sent back to us.
const fields = "presentationId,pageSize,title"
+ ",slides(objectId,pageType,pageElements(objectId,size,title,description))"
+ ",masters(objectId,pageType,pageElements(objectId,size,title,description))"
+ ",layouts(objectId,pageType,pageElements(objectId,size,title,description))";
const createdMetadata = Slides.Presentations.create(newPres, {fields: fields});
console.log({message:"Created a Presentation", response: createdMetadata});
return createdMetadata;
}
function addSlide_(pId) {
const response = Slides.Presentations.batchUpdate({ requests: [{ createSlide: {} }] }, pId);
return response.replies[0].createSlide.objectId;
}
function foo() {
const pres = createPresentation_();
const newSlideId = addSlide_(pres.presentationId);
// Get requests to add and to modify tables.
const openingTableRq = makeCreateTableRequest_(pres.slides[0].objectId, 2, 4);
const newTableRq = makeCreateTableRequest_(newSlideId, 7, 4, true);
const changeWidthRq = makeModifyTableColumnPropsRequest_(newTableRq.createTable.objectId, {magnitude: 80, unit: "PT"}, [0]);
// Add and update the desired table, then create a new slide.
var response = Slides.Presentations.batchUpdate({
requests: [
openingTableRq, // will have reply
newTableRq, // will have reply
changeWidthRq, // no reply
{ createSlide: {} } // will have reply
]
}, pres.presentationId);
console.log({message: "Performed updates to the created presentation", response: response});
}

Insert YouTube top level comment using Google Apps Script

I am trying to create a program using Google Apps Script that inserts a comment when a certain YouTube channel uploads. I have been able to get the latest YouTube video ID from the channel but when I try to insert a comment, it throws an error, "Parse Error (line 19, file 'Code')".
Line 19: YouTube.CommentThreads.insert("snippet", {
Here's my code:
function getVideo() {
// MrBeast Channel ID: UCX6OQ3DkcsbYNE6H8uQQuVA
var channel = "UCX6OQ3DkcsbYNE6H8uQQuVA";
var fttx = "FIRST!";
var results = YouTube.Channels.list("contentDetails", {"id": channel});
for (var i in results.items) {
var item = results.items[i];
var playlistId = item.contentDetails.relatedPlaylists.uploads;
// Uploads Playlist ID: UUX6OQ3DkcsbYNE6H8uQQuVA
var playlistResponse = YouTube.PlaylistItems.list("snippet", {"playlistId": playlistId, "maxResults": 1});
for (var j = 0; j < playlistResponse.items.length; j++) {
var playlistItem = playlistResponse.items[j];
var latvid = playlistItem.snippet.resourceId.videoId;
comment(latvid, channel, fttx);
}
}
}
function comment(vid, ytch, fc) {
YouTube.CommentThreads.insert("snippet", {
"snippet.channelId": ytch,
"snippet.videoId": vid,
"snippet.topLevelComment.snippet.textOriginal": fc
});
}
Per Apps Script advanced services documentation, when specifying resources (such as a CommentThread) they are the first parameter to a method. If you use the Apps Script editor's autocomplete, it is very clear about the required order:
Also note that you have incorrectly created your resource body - you have various sub-properties. For example, the snippet property is a required member of the CommentThread resource. Three "snippet.___" properties are not equivalent to one snippet property with 3 sub-properties.
Thus the solution to resolve the parse error in YouTube.CommentThreads.insert is to use the required method signature, with the required resource format:
function startCommentThread(vid, ytch, fc) {
const resource = {
snippet: {
channelId: ytch,
videoId: vid,
topLevelComment: {
snippet: {
textOriginal: fc
}
}
}
};
YouTube.CommentThreads.insert(resource, "snippet");
}
According to docs the {}, is missing and single quotes are used. I'm unable to test this right now but hopefully it will solve your problem.
commentThreadsInsert('snippet',
{},
{'snippet.channelId': '',
'snippet.videoId': '',
'snippet.topLevelComment.snippet.textOriginal': ''
});