How to create and send a threaded message in Google Chat with ruby - ruby

I will like to be able to be able send threaded messages in Google Chat. This is the code I have so far. It works fine in sending regular messages.
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'google/apis/chat_v1'
scope = 'https://www.googleapis.com/auth/chat.bot'
chat = Google::Apis::ChatV1::HangoutsChatService.new
chat.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: File.open('./credentials.json'),
scope: scope)
msg = Google::Apis::ChatV1::Message.new(text: "Nice")
chat.create_space_message("space_name", msg)
I have tried reading this doc but cant quite figure it out.
Following this doc , I got the python script to work like this
from google.oauth2 import service_account
from googleapiclient.discovery import build
SCOPES = ['https://www.googleapis.com/auth/chat.bot']
creds = service_account.Credentials.from_service_account_file(
'./credentials.json', scopes=SCOPES)
chat = build('chat', 'v1', credentials=creds)
result = chat.spaces().messages().create(
parent='space_name',
messageReplyOption='REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD',
body={
'text': 'works!',
'thread': {
'threadKey': 'nameOfThread'
}
}
).execute()
print(result)
I will like it to work for ruby. I am using ruby gem gem 'google-api-client', '~> 0.34'

Related

Every error in the book from google-api-ruby-client but no data

I have been attempting to work on a request from my boss this week that requires using the google admin directory api.
At this point I am questioning if what I am trying to do is even possible.
Can I retrieve data from the scope "https://www.googleapis.com/auth/admin.directory.device.mobile.readonly" with a service account? Is it even possible?
The errors I have seen in the past hour are below...
Many of them sound the same and I have no idea what is going on or why this is such a difficult journey for such basic information.
PERMISSION_DENIED: Request had insufficient authentication scopes. (Google::Apis::ClientError)
`check_status': Unauthorized (Google::Apis::AuthorizationError)
Authorization failed. Server message: (Signet::AuthorizationError)
{
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested."
}
`check_status': permission_denied: request had insufficient authentication scopes
`check_status': badRequest: Bad Request
My current test script is below...
require "google/apis/admin_directory_v1"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"
APPLICATION_NAME = "Directory API Ruby Quickstart".freeze
CREDENTIALS_PATH = "credentials.json".freeze
CUSTOMER_ID = "thasgunnabeanopefrommedawg".freeze
SCOPE = ["https://www.googleapis.com/auth/admin.directory.device.mobile.readonly"].freeze
authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io:
File.open('credentials.json'),
scope: SCOPE)
authorizer.update!(sub: "fullbl00m#citadelny.com")
authorizer.fetch_access_token!
# puts authorize
# Initialize the API
service = Google::Apis::AdminDirectoryV1::DirectoryService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = Google::Auth.get_application_default(SCOPE)
response = service.list_mobile_devices(customer_id: CUSTOMER_ID)
puts response.to_json
EDITS BELOW *** [27th, MAY, 2022]
I have been trying with ruby, python, and postman for two weeks at this point :/
Last night I took the ruby snippet that was posted by user:Daimto below.
I was able to return a token with the following modified version of the ruby snippet provided in the answer below.
require 'googleauth'
require 'google/apis/admin_directory_v1'
creds = {
"type": "service_account",
"project_id": "MYPROJECTNAME",
"private_key_id": "MYPRIVATEKEYID",
"private_key": "-----BEGIN PRIVATE KEY-----\n-MY PRIVATE KEY
WILL BE HERE BUT REMOVED FOR SECURITY-----END PRIVATE KEY-----\n",
"client_email": "emailfromserviceaccount-compute#developer.gserviceaccount.com",
"client_id": "MYCLIENTIDISACTUALLYHERE",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/compute%40developer.gserviceaccount.com"
}
creds_json = creds.to_json
creds_json_io = StringIO.new(creds_json)
auth = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: creds_json_io,
scope["https://www.googleapis.com/auth/admin.directory.device.mobile.readonly","https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly","https://www.googleapis.com/auth/admin.directory.device.mobile"]
)
auth.sub = "emailfrommyserviceaccount-
compute#developer.gserviceaccount.com"
puts auth.fetch_access_token
Please excuse the formatting.
I took the service account out of the env variable for now to make sure I can get it to work without adding extra layers of abstraction at this time.
When trying to add the additional code from the Directory Api Quickstart to the above snip I STILL RETURN THE ERROR
/var/lib/gems/2.7.0/gems/google-apis-core-0.5.0/lib/google/apis/core/http_command.rb:224:in `check_status': Unauthorized (Google::Apis::AuthorizationError)
The additional code added is below...
The last line of the previous snip gets changed to the first line of the snip that comes after this. This is to properly pass the token to the example after modifying user:Daimto's response.
authorize = auth.fetch_access_token
# Initialize the API
service = Google::Apis::AdminDirectoryV1::DirectoryService.new
service.client_options.application_name = "my-application-name"
service.authorization = authorize
# List the first 10 users in the domain
response = service.list_users(customer: "my_customer",
max_results: 10,
order_by: "email")
puts "Users:"
puts "No users found" if response.users.empty?
response.users.each { |user| puts "- #{user.primary_email} (#{user.name.full_name})" }
The method Method: mobiledevices.list requires one of the following scopes.
So to answer your first question yes you can use the https://www.googleapis.com/auth/admin.directory.device.mobile.readonly scope.
Error number 1
PERMISSION_DENIED: Request had insufficient authentication scopes.
You were probably getting this error when you had supplied a different scope.
Error 3;
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
There are three types of clients you can create on google cloud console.
web client
native client
service account
The json file you get from creating these clients is all different. The code that uses them is also different. The error is telling you that you have a client.json file that you are using which does not match the type of code you are using.
How to create service account credetinals
The code for a service account would be like this Not tested you may need to fix the scope. Remember that the service account needs to be configured properly on your workspace domain for the sub to work.
require 'googleauth'
require 'google/apis/admin_v1'
creds = ENV['GOOGLE_SERVICE_ACCOUNT'] # JSON downloaded from cloud console
# is saved in this ENV variable
creds_json = JSON.parse(creds)
creds_json_io = StringIO.new(creds_json.to_json)
auth = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: creds_json_io,
scope: [Google::Apis::ADMINV1::ADMIN_DIRECTORY_MOBILE_READONLY]
)
auth.sub = 'admin#yourdomain.com'
auth.fetch_access_token
Tip: You have a lot of errors there, I feel that you have been struggling with this for a while. Advice step back, have a look at the sample on the readme for the Google-api-ruby-client. Start over. Just get your auth to work. Once you get the code right and the client right all the pieces will fit into place.

Http 403 Error: Details Request had insufficient authentification scopes Google Classroom Announcements

I am using Python Google Classroom API to retrieve announcements data.
Here is my code.
from fetch import Fetch
from googleapiclient.discovery import build
cred = 'catp.json'
get_credits = Fetch(cred) #fetching credential data
credit = get_credits()
service = build('Classroom', 'v1', credentials=credit)
setup = service.courses()
data = setup.list().execute()['courses']
course_names = []
course_ids = []
for i in range(len(data)):
course_names.append(data[i]['name'])
course_ids.append(data[i]['id'])
announcement_data = setup.announcements().list(courseId=course_ids[0]).execute()
But I receive the following Traceback Error:
Additional Information:
My project is registered under service account.
My role is Owner.
I have students account on Google Classroom.
To check whether the same error would be called if I tried to access announcements from a teachers account I created a Course in Classroom, using my Students account and posted some demo announcements.
The result was the same TracebackError. I also tried getting access to the data using API Explorer from Google, passing the same course ID as an argument. The data was received normally without any errors.
[Edit]
Here is the code for fetching credentials, Fetch(cred):
import os
import pickle
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
class Fetch:
def __init__ (self, credential_filename):
self.scopes = ['https://www.googleapis.com/auth/classroom.courses.readonly',
'https://www.googleapis.com/auth/classroom.announcements',
]
self.path = 'C:/frank/programs/python/google api'
self.credential_file = credential_filename
def __call__(self):
os.chdir(self.path)
token = open('token.pickle', 'rb')
creds = pickle.load(token)
if creds.valid == False:
if creds.expired == True:
creds.refresh(Request())
else:
try:
flow = InstalledAppFlow.from_client_secrets_file(self.credential_file, self.scopes)
creds = flow.run_local_server(port=0)
except FileNotFoundError:
print(f'{self.credential_file} does not exist')
token = open(self.token_file, 'wb')
pickle.dump(creds, token)
return creds

Auth and get running instance count for azure in ruby

How can I get the running instance count for Azure in ruby, I'm looking something equivalent to How to enumerate running ec2 instances and load them into a database using ruby? in AWS.
Thanks
Here is what I did:
subscription_id= 'xyz'
provider = MsRestAzure::ApplicationTokenProvider.new(tenant_id, client_id, secret)
credentials = MsRest::TokenCredentials.new(provider)
client = Azure::ARM::Resources::ResourceManagementClient.new(credentials)
client.subscription_id = subscription_id
resource_group_params = Azure::ARM::Resources::Models::ResourceGroup.new()
resource_group_params.location = 'westus'
promise = client.resource_groups.create_or_update('new_test_resource_group',resource_group_params)
result = promise.value!
resource_group_params = result.body
p resource_group_params.name
p resource_group_params.id
Response:
azureAuth.rb:35:in <main>': undefined methodvalue!' for # (NoMethodError)
From the description, it is hard to know this "running instance". Do you mean Azure web app or Azure cloud service instance? Based on your code snippet, you seem want to create Azure resource group and get some related info. For this scenario, I suggest you follow the sample code in Azure official site: Manage Azure resources and resource groups with Ruby. Hope it could give you some tips.
[Update]
I am sorry for that I did not test the sample code and give solution to you.
still it is throwing error "nil:NilClass (NoMethodError)
Based on my test, I think you did not create azure resource group, so the promise object is nil. Please try list resource group operation client.resource_groups.list.each{ |group| print_item(group) } I think you will get nil issue too. When I dig into the ms_rest_azure I could not even able to get token. When I change below code (application_token_provider.rb) I could able to get the token
1) request_body['{resource_uri}'] = "https%3A%2F%2Fmanagement.azure.com%2F" #ERB::Util.url_encode(#settings.token_audience). Use management.azure.com instead of management.core.windows.net
2) response = connection.post do |request| get method change to post method.
3) :ssl => MsRest.ssl_options change to :ssl => {:verify => false}
I would suggest you use rest API to achieve the same purpose. Below is my Ruby code based on rest API. Hope it helps.
connection = Faraday.new(:url => 'https://login.windows.net/<telnet id>/oauth2/token',:ssl => {:verify => false}) do |builder|
builder.adapter Faraday.default_adapter
end
response = connection.post do |request|
request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
request.body = 'resource=https%3A%2F%2Fmanagement.azure.com%2F&client_id=<your client id>&client_secret=<your client secret>&grant_type=client_credentials'
end
response_body = JSON.load(response.body)
#token = response_body['access_token']
getResrouceCon = Faraday.new(:url => 'https://management.azure.com/subscriptions/<subscriotion id>/resourcegroups?api-version=2015-01-01',:ssl => {:verify => false}) do |builder|
builder.adapter Faraday.default_adapter
end
response_resource = getResrouceCon.get do |req|
req.headers["Authorization"] = "bearer #{#token}"
end
puts "response #{response_resource.body}"
Note: I use the latest version azure_mgmt_resource
[Update]
We could find Azure classic cloud service from Azure resource portal. we can get a lot of useful info of Azure cloud service from configuration. here is the screenshot:
If you want to get roles and role instance. We can use this rest API in Azure resource portal https://management.azure.com/subscriptions/<subscription id>/resourceGroups/<group name>/providers/Microsoft.ClassicCompute/domainNames/<cloud service name>/slots/Production?api-version=2016-04-01
We only need to modify a little from my previous answer. Here is the code that could get cloud service configuration:
getResrouceCon = Faraday.new(:url => 'https://management.azure.com/subscriptions/<subscription id>/resourceGroups/<group name>/providers/Microsoft.ClassicCompute/domainNames/<cloud service name>/slots/Production?api-version=2016-04-01',:ssl => {:verify => false}) do |builder|
builder.adapter Faraday.default_adapter
end
response_resource = getResrouceCon.get do |req|
req.headers["Authorization"] = "bearer #{#token}"
end
puts "response #{response_resource.body}"

How to use this gem 'google-api-client' without requiring me to login my gmail account on every browser?

I'm developing (Ruby) Sinatra Application.
I currently used two gems:
gem 'google_drive'
gem 'google-api-client'
Here is the link i'm following google-drive-ruby and documentation.
Why I cannot get any data via Google API, once my gmail was NOT logged in to browser?
Even I provided the required credentials:
client_id
client_server
scope
refresh_token
I successfully got data when my gmail was logged in on the same browser I'm displaying the image from Google Drive.
Opening Connection:
require "rubygems"
require "google/api_client"
require "google_drive"
client = Google::APIClient.new(:application_name => 'Google Api Client', :application_version => '1.0')
auth = client.authorization
auth.client_id = xxx
auth.client_secret = xxx
auth.scope = xxx
auth.redirect_uri = "http://localhost"
auth.refresh_token = xxx
auth.fetch_access_token!
session = GoogleDrive.login_with_oauth(auth.access_token)
#session = GoogleDrive.saved_session(#config_file)
config.json
{
"client_id": "xxx-xxx.apps.googleusercontent.com",
"client_secret": "xxx",
"scope": [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive.file",
"https://spreadsheets.google.com/feeds/"
],
"refresh_token": "xxx"
}
Please help, on how to use this gem WITHOUT requiring me to login my gmail account on every browser.

omniauth oauth tokens for gmail are invalid

I'm trying to get an oauth token I can use with gmail_xauth (ruby gem)
to look at a user's mail. I first registered my app with google and
then set up devise to request access to mail:
config.omniauth :google, 'key', 'secret', :scope => 'https://mail.google.com/mail/feed/atom/'
I then go through the outh/openid flow and google prompts me to
approve access to gmail, redirecting me back to the app with a a token
and secret in the omniuth credentials & my Google account lists my app
as authorized to access my data. So far so good.
Now, when I take those credentials and try to use them with
gmail_xoauth like so:
require 'gmail_xoauth'
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs =
nil, verify = false)
imap.authenticate('XOAUTH', '...#gmail.com',
:consumer_key => 'key,
:consumer_secret => 'secret',
:token => 'omniauth_returned_token',
:token_secret => 'omniauth_returned_secret'
)
I get an error "Net::IMAP::NoResponseError: Invalid credentials
(Failure)".
Interestingly, following the gmail_xoauth README to generate a token
with an same consumer using a python script it does work.
This works for me:
config.omniauth :google, 'anonymous', 'anonymous', :scope => 'https://mail.google.com/'
I'm using the gmail gem, so to connect it looks like this:
gmail = Gmail.connect(:xoauth, auth.uid,
:token => auth.token,
:secret => auth.secret,
:consumer_key => 'anonymous',
:consumer_secret => 'anonymous'
)
I'm passing an authentication object in, but you'll be getting it from the env variable env["omniauth.auth"]. I'm using anonymous/anonymous for the key/secret since I haven't registered my domain with google, but I believe you can here. It'll still work with anonymous/anonymous, but Google will just warn the user.
Google's OAuth1 protocol is now deprecated and many gems have not yet updated to use their OAuth2 protocol. Here is a working example of fetching email from Google using their OAuth2 protocol. This example uses the mail, gmail_xoauth, omniauth, and omniauth-google-oauth2 gems.
You will also need to register your app in Google's API console in order to get your API tokens.
# in an initializer:
ENV['GOOGLE_KEY'] = 'yourkey'
ENV['GOOGLE_SECRET'] = 'yoursecret'
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], {
scope: 'https://mail.google.com/,https://www.googleapis.com/auth/userinfo.email'
}
end
# ...after handling login with OmniAuth...
# in your script
email = auth_hash[:info][:email]
access_token = auth_hash[:credentials][:token]
imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
imap.authenticate('XOAUTH2', email, access_token)
imap.select('INBOX')
imap.search(['ALL']).each do |message_id|
msg = imap.fetch(message_id,'RFC822')[0].attr['RFC822']
mail = Mail.read_from_string msg
puts mail.subject
puts mail.text_part.body.to_s
puts mail.html_part.body.to_s
end

Resources