I'm writing a Discord Bot (Discord.net) and I found myself requiring to access some data on a google sheet using their APIs. Being that I thought it would be best to actually separate those two in two different class files I have tried summoning the Main method of the Google APIs into my program (after having renamed it "Sheets") like this in my Program.cs:
using Discord;
using Discord.WebSocket;
using System;
using System.IO;
using System.Threading.Tasks;
namespace WoM_Balance_Bot
{
public class Program
{
public static void Main(string[] args)
{
GoogleAPI GSheet = new GoogleAPI();
GSheet.Sheets();
new Program().MainAsync().GetAwaiter().GetResult();
}
private DiscordSocketClient _client;
public async Task MainAsync()
{
_client = new DiscordSocketClient();
_client.MessageReceived += CommandHandler;
_client.Log += Log;
var token = File.ReadAllText("bot-token.txt");
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
// Block this task until the program is closed.
await Task.Delay(-1);
}.......ecc
I tried writing the parameters to pass here in these parentheses like "string" and "args" but either I get the syntax wrong or I have a very wrong idea about what to pass exactly.
This is the actual content of GoogleAPI.cs, which is the other class file I created that has the Google Sheet APIs:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Util.Store;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace WoM_Balance_Bot
{
public class GoogleAPI
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-dotnet-quickstart.json
private static readonly string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
private static readonly string ApplicationName = "wombankrolls";
public static void Sheets(string[] args)
{
UserCredential credential;
Console.WriteLine("if you read this then it's good");
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request parameters.
String spreadsheetId = "16W56LWqt6wDaYAU5xNdTWCdaY_gkuQyl4CE1lPpUui4";
String range = "Class Data!G163:I";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(spreadsheetId, range);
// Prints the names and majors of students in a sample spreadsheet:
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
ValueRange response = request.Execute();
IList<IList<Object>> values = response.Values;
/*
if (values != null && values.Count > 0)
{
Console.WriteLine("Name, Major");
foreach (var row in values)
{
// Print columns A and E, which correspond to indices 0 and 4.
Console.WriteLine("{0}, {1}", row[0], row[4]);
}
}
else
{
Console.WriteLine("No data found.");
}
Console.Read();
*/
}
}
}
I have modified it from the quickstart given by Google in a way that I thought it made sense but I still get in the end the same error:
There is no argument given that corresponds to the required formal parameter 'args' of 'GoogleAPI.Sheets(string[])'
as the user "David L" wrote in the comments:
As a general rule of thumb, if you do not use an argument, remove it. C# helps enforce this paradigm by throwing a compiler error if your method expects an argument and you do not provide one, which is exactly what is happening here.
It was my bad as I was under the impression of the total opposite during an API implementation. I would like to always target a clean code as a result and keeping stuff that I will not use was my bad. Thank you David!
Related
I am working on a console based search tool for "Warrants" in a game I play that searches off of a google sheet from the google sheets API and C#. Originally I made this on python and it worked perfectly but I had a lot of issues distributing my python file so I moved to C#.
The API is calling the data perfectly fine and I am able to present a list of all the data I was seeking on launch, but when I try and save it to list files inside my program I get the following:
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
I also added in a section that tells me the data type I am calling with row[1] and it says (only one "```", had to double to format):
System.Collections.Generic.List``1[System.Object]
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Diagnostics;
namespace WarrantSearchProgram
{
class Program
{
static readonly string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
static readonly string ApplicationName = "WarrantSearchProgram";
static readonly string SpreadsheetId = "SpreadsheetId";
static readonly string sheet = "Imported Data";
static SheetsService service;
//List of Warrant Column Variables... Only nameList is being used for now
public static IList<object> testOBJ;
public static List<object> wtStatus;
public static List<object> wtType;
public static List<object> wtNum;
public static IList<object> nameList;
public static List<object> wtCivName;
public static List<object> wtDOB;
public static List<object> wtAddress;
public static List<object> wtJs;
public static List<object> wtCharges;
public static List<object> wtEvidence;
public static List<object> wtReqOfc;
public static List<object> wtReqOfcNum;
static void Main(string[] args)
{
//Set console color and other general settings
Console.Title = "DOJ Warrant Search Program UNOFFICIAL";
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.Green;
// Initialization of creds and google sheets
GoogleCredential credential;
using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(Scopes);
}
// Create Google Sheets API service.
service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
//First initilization of warrant sheet info, creates and manages variables.
UpdateSheetData();
while (true)
{
// Main repeating text and SEARCH INPUT
Console.WriteLine("-----------------------------------------------");
Console.WriteLine("Please type in a full name to search for warrants.");
Console.WriteLine("Only ACTIVE warrants will be shown.");
Console.WriteLine("Type in a warrant number to show extra info, including evidence, on just that warrant");
Console.WriteLine("-----------------------------------------------");
Console.Write("Search >>> ");
string searchName = Console.ReadLine();
searchName = searchName.ToUpper();
Console.WriteLine();
Console.Beep();
Console.Clear();
}
}
static void UpdateSheetData()
{
var range = $"{sheet}!A:F";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
IList<IList<object>> values = response.Values;
if (values != null && values.Count > 0)
{
foreach (var row in values)
{
// Calls the row (2nd, name) and displays each name in list
Console.WriteLine("{0}", row[1]);
Console.WriteLine(row.GetType().ToString());
// Attempts to build list of names in program ERROR HERE
nameList.Add(row[1]);
}
}
else
{
Console.WriteLine("No data found.");
}
}
}
}
I removed sections of the code that have nothing to do with this so its easier to read...
As you can tell, I tried IList<object>, List<object>, and List<string> at different times and it didn't work for me.
My goal here is to load each column of data into a list that I can then perform searches on, index, and display matching data from other lists. This is all not very difficult to do once I can load the data up into the program and separate it.
error at row 98
As per your comment, at line 98 you are trying to Add a value to the nameList, which may not be initialized. This is why you get the error you do, Object reference not set to an instance of an object. - The nameList wasn't initiated, so you can't call it's internal functions. You might want to instantiate it, anywhere before you call UpdateSheetData, e.g:
nameList = new List<object>();
I'm getting this error from Azure IoT Provisioning Service when creating the provisioning client.
System.ArgumentException: 'Should specify SharedAccessKeyName'
I copied the Connection String from the Portal, what could be wrong?
using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Provisioning.Service;
namespace EnrollmentApp
{
class Program
{
private static string ProvisioningConnectionString = "HostName=happybeerhub-us.azure-devices.net;DeviceId=test-device-01;SharedAccessKey=tawpddfqUt3EHZg9a5tUzQ5fjros7zMhKsZbmuXzwXE=";
private static string EnrollmentGroupId = "test";
private static string X509RootCertPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), #"key.pfx");
static void Main(string[] args)
{
RunSample().GetAwaiter().GetResult();
Console.WriteLine("\nHit <Enter> to exit ...");
Console.ReadLine();
}
public static async Task RunSample()
{
Console.WriteLine("Starting sample...");
using (ProvisioningServiceClient provisioningServiceClient =
ProvisioningServiceClient.CreateFromConnectionString(ProvisioningConnectionString))
{
#region Create a new enrollmentGroup config
Console.WriteLine("\nCreating a new enrollmentGroup...");
var certificate = new X509Certificate2(X509RootCertPath);
Attestation attestation = X509Attestation.CreateFromRootCertificates(certificate);
EnrollmentGroup enrollmentGroup =
new EnrollmentGroup(
EnrollmentGroupId,
attestation)
{
ProvisioningStatus = ProvisioningStatus.Enabled
};
Console.WriteLine(enrollmentGroup);
#endregion
#region Create the enrollmentGroup
Console.WriteLine("\nAdding new enrollmentGroup...");
EnrollmentGroup enrollmentGroupResult =
await provisioningServiceClient.CreateOrUpdateEnrollmentGroupAsync(enrollmentGroup).ConfigureAwait(false);
Console.WriteLine("\nEnrollmentGroup created with success.");
Console.WriteLine(enrollmentGroupResult);
#endregion
}
}
}
}
Well you are missing the SharedAccessKeyName so the validation tries to check it and as it is null in your case you get the exception.
As you can see in the ServiceConnectionStringBuilder.cs the correct connection string format is
/// A valid connection string shall be in the following format:
/// <code>
/// HostName=[ServiceName];SharedAccessKeyName=[keyName];SharedAccessKey=[Key]
/// </code>
Also in line 128 you can see the check which throws your exception:
if (string.IsNullOrWhiteSpace(SharedAccessKeyName))
{
throw new ArgumentException("Should specify SharedAccessKeyName");
}
Now you might think that ServiceConnectionStringBuilder.cs is not used by your code, but the call ProvisioningServiceClient.CreateFromConnectionString creates a new ProvisioningServiceClient instance, which in turn calls ServiceConnectionStringBuilder.Parse().
For more see ProvisioningServiceClient.cs in lines 82, 113 and 123.
SOLUTION:
To solve this you have to provide a SharedAccessKeyName. You should also consider using the IotHubConnectionStringBuilder for this which already checks your properties whhile building the connection string.
The questioner pointed out the key name is the deviceId according to this blog post here. Thus, the solution is to use SharedAccessKeyname=xx instead of DeviceId=xx.
I am just stuck with aws s3 on my .net core mvc application. I just simply need to input bucket name of s3 then return all of directory name list in this bucket but this simple task i didn't found anywhere on internet. I already tried few solution provided by AWS forum but problem is this absolutely not works at all. Bellow i have provided my controller code also forum link. Actually the issue they told is Amazon.S3.IO and S3DirectoryInfo namespace was removed from .net core so i am failed to follow them as they advised there. Any one can fix my code bellow which will give a list of bucket directory in .net core application?
I am using two nuget package-
AWSSDK.Core and AWSSDK.S3
Forum Link - Amazon.S3.IO not supported in .Net Core anymore?
Controller:
using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
public IActionResult Media()
{
string bucketName = "domain33.com";
AmazonS3Client s3Client = new AmazonS3Client("Access_Key_ID", "Secret_Access_Key", RegionEndpoint.USEast1);
var getResponse = s3Client.ListBucketsAsync(new GetObjectRequest
{
BucketName = bucketName
});
var x = getResponse;
return View();
}
You could try using the ListObjectsV2Async method on IAmazonS3 to retrieve a list of all of the existing objects in the bucket based on the AWS's example. Their code is below in case the link dies:
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-s3-developer-guide/blob/master/LICENSE-SAMPLECODE.)
using Amazon.S3;
using Amazon.S3.Model;
using System;
using System.Threading.Tasks;
namespace Amazon.DocSamples.S3
{
class ListObjectsTest
{
private const string bucketName = "*** bucket name ***";
// Specify your bucket region (an example region is shown).
private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2;
private static IAmazonS3 client;
public static void Main()
{
client = new AmazonS3Client(bucketRegion);
ListingObjectsAsync().Wait();
}
static async Task ListingObjectsAsync()
{
try
{
ListObjectsV2Request request = new ListObjectsV2Request
{
BucketName = bucketName,
MaxKeys = 10
};
ListObjectsV2Response response;
do
{
response = await client.ListObjectsV2Async(request);
// Process the response.
foreach (S3Object entry in response.S3Objects)
{
Console.WriteLine("key = {0} size = {1}",
entry.Key, entry.Size);
}
Console.WriteLine("Next Continuation Token: {0}", response.NextContinuationToken);
request.ContinuationToken = response.NextContinuationToken;
} while (response.IsTruncated);
}
catch (AmazonS3Exception amazonS3Exception)
{
Console.WriteLine("S3 error occurred. Exception: " + amazonS3Exception.ToString());
Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.ToString());
Console.ReadKey();
}
}
}
}
Based on that sample, you could do further processing or add the keys to a list of strings for subsequent processing, instead of just writing it the console as their example code does. For instance, you could add each key to a list, and then process that list to calculate the distinct "directories".
I;m currently working on a project that requires me to read and write data from google sheets. Hence, I started with the Google Sheets Api (v4) Quick start Tutorial for C#. I'm using Visual Studio 2017 and have the latest versions of all nuget packages.
I have followed the tutorial step-by-step, but instead of creating a Console Application, I created a Windows Form Application. The code and other steps were followed exactly as written.
When run inside the IDE, the code runs as expected. However, when published and then run outside the IDE, the system cannot find System.Net.HTTP.
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in WindowsFormsApp1.exe
Could not load file or assembly 'System.Net.Http, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.The system cannot find the file specified.
After researching and trying the solutions posted in other similar questions (which did not work) I landed on a StackOverflow post suggest redirecting older version of System.Net.Http to the latest version. However, this did not work.
App.config with redirection code (screenshot)
Here is my current code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.IO;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-dotnet-quickstart.json
static string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
static string ApplicationName = "Google Sheets API .NET Quickstart";
public Form1()
{
InitializeComponent();
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/sheets.googleapis.com-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request parameters.
String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms";
String range = "Class Data!A2:E";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(spreadsheetId, range);
// Prints the names and majors of students in a sample spreadsheet:
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
ValueRange response = request.Execute();
IList<IList<Object>> values = response.Values;
if (values != null && values.Count > 0)
{
Console.WriteLine("Name, Major");
foreach (var row in values)
{
// Print columns A and E, which correspond to indices 0 and 4.
Console.WriteLine("{0}, {1}", row[0], row[4]);
}
}
else
{
Console.WriteLine("No data found.");
}
Console.Read();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
Any help to fix this problem would be much appreciated. Thanks!
I am trying to get data from google sheets. For example, let's say I have a sheet "test" and I want to get cell a2 from "test", and so I save the contents of cell a2 as a string.
I believe Google Sheets Api v4 (https://developers.google.com/api-client-library/dotnet/apis/sheets/v4) is what I need to use, but I am not sure how to use it.
I have also found similar question posts on stack overflow, but they all seem outdated.
Finally, I have tried this tutorial (https://developers.google.com/sheets/api/quickstart/dotnet#step_2_prepare_the_project) with my c# visual studio, but I get error messages (which I can include if it is helpful).
So, my question is, how do extract data from a google sheet, using the API or some other tool. Thanks for the help, and please notify me if my question needs editing to better fit this community.
edit: So, following the tutorial, I have this code:
using System;
using System.Windows.Forms;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GoogleSheetsTest1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
class Program
{
static string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
static string ApplicationName = "Google Sheets API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/sheets.googleapis.com-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request parameters.
String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms";
String range = "Class Data!A2:E";
SpreadsheetsResource.ValuesResource.GetRequest request =
service.Spreadsheets.Values.Get(spreadsheetId, range);
// Prints the names and majors of students in a sample spreadsheet:
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
ValueRange response = request.Execute();
IList<IList<Object>> values = response.Values;
if (values != null && values.Count > 0)
{
Console.WriteLine("Name, Major");
foreach (var row in values)
{
// Print columns A and E, which correspond to indices 0 and 4.
Console.WriteLine("{0}, {1}", row[0], row[4]);
}
}
else
{
Console.WriteLine("No data found.");
}
Console.Read();
}
}
}
}
The error message I am getting is:
Error CS0017 Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point.
I realize this is probably a really stupid question and I hate to be asking for help here, but I am not sure how to use the google api code, so any help/explanation (so I can understand what the fix is) would be really appreciated. Thanks