I'm following the tutorial here https://slackapi.github.io/node-slack-sdk/bots#posting-a-message and I'm flummoxed why I can't get this portion of the tutorial code to work. I copied and pasted the code from this section, which is below
var RtmClient = require('#slack/client').RtmClient;
var RTM_CLIENT_EVENTS = require('#slack/client').CLIENT_EVENTS.RTM;
var bot_token = process.env.SLACK_BOT_TOKEN || ''; //I know the problem is not here.
var rtm = new RtmClient(bot_token);
rtm.start();
var channel = "#general"; //could also be a channel, group, DM, or user ID (C1234), or a username (#don)
// you need to wait for the client to fully connect before you can send messages
rtm.on(RTM_CLIENT_EVENTS.RTM_CONNECTION_OPENED, function () {
rtm.sendMessage("Hello!", channel);
});
Since the first section of the tutorial code worked, the problem is definitely coming from the last 3 lines of code. Presumably its an issue with the event. My error message is
Unhandled rejection Error
at RTMClient.handleMessageAck [as _handleMessageAck] (/Users/mg/projects/slack_projects/games/s
lack_connect_four/node_modules/#slack/client/lib/clients/rtm/client.js:496:40)
at RTMClient._handleWsMessageViaEventHandler (/Users/mg/projects/slack_projects/games/slack_con
nect_four/node_modules/#slack/client/lib/clients/rtm/client.js:459:12)
at RTMClient.handleWsMessage (/Users/mg/projects/slack_projects/games/slack_connect_four/node_m
odules/#slack/client/lib/clients/rtm/client.js:419:10)
at WebSocket.wrapper (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/l
odash/lodash.js:4968:19)
at emitTwo (events.js:106:13)
at WebSocket.emit (events.js:191:7)
at Receiver.ontext (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/ws/
lib/WebSocket.js:841:10)
at /Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/ws/lib/Receiver.js:5
36:18
at Receiver.applyExtensions (/Users/mg/projects/slack_projects/games/slack_connect_four/node_mo
dules/ws/lib/Receiver.js:371:5)
at /Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/ws/lib/Receiver.js:5
08:14
at Receiver.flush (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/ws/l
ib/Receiver.js:347:3) at Receiver.finish (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/ws/
lib/Receiver.js:541:12)
at Receiver.expectHandler (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modu
les/ws/lib/Receiver.js:499:31)
at Receiver.add (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modules/ws/lib
/Receiver.js:103:24)
at TLSSocket.realHandler (/Users/mg/projects/slack_projects/games/slack_connect_four/node_modul
es/ws/lib/WebSocket.js:825:20)
at emitOne (events.js:96:13)
I would really appreciate any help.
Possibly your bot did not join the #general channel yet. Invite him to the channel first.
This post might be old but I'd like to share my experience with this error. I was testing this code too and I was using a private channel. Even if the bot is already a member of the channel it throws this error. Then I tried to use a public channel then it went through. I hope this helps.
You cannot use channel names, usernames, or user ids. Use channel/group/DM ids instead.
Change:
var channel = "#general";
To:
var channel = "C--------";
You can grab this channel ID from your channel's URL:
https://yourworkspace.slack.com/messages/C-------/details/
And your bot must be added to the target channel, as detailed here:
On your app's settings page, click the OAuth & Permissions settings item in the navigation menu.
In the Scopes section, add the chat:write permission scope, then click Save Changes.
Now that you've changed the scopes for your app, you'll need to install it again - you should see a yellow banner near the top of the screen telling you to click here to reinstall your app. Click it, and follow through the permissions authorization page.
You'll be redirected back to the OAuth & Permissions page, where you can see your workspace token listed at the top of the page - store this for use later on.
SLACK API REFERENCE
This code will work as expected:
var RtmClient = require('#slack/client').RtmClient;
var CLIENT_EVENTS = require('#slack/client').CLIENT_EVENTS;
var rtm = new RtmClient('.....'); // your token
rtm.start();
let channel = 'C--------' ; //your channel
rtm.on(CLIENT_EVENTS.RTM.RTM_CONNECTION_OPENED, function () {
rtm.sendMessage("Hello stack!", channel);
});
Related
Starting with a sample oauth app I am trying to retrieve info about an online meeting that occurred.
Create the client:
var graphClient = _graphServiceClientFactory.GetAuthenticatedGraphClient((ClaimsIdentity)User.Identity);
Make request:
var returnObj = await graphClient.Me.OnlineMeetings.CreateOrGet(meetingID).Request().PostAsync();
The problem is the lack of information returned. I am trying to retrive the chat from the meeting, which seems like it should be in returnObj.ChatInfo but this is all I get back:
{
"threadId":"19:meeting_SOMELONGUNIQUESTRINGHERE#thread.v2",
"messageId":"0",
"#odata.type":"microsoft.graph.chatInfo"
}
Also missing are the attendees in Participants (count=0). I know there are non zero attendees and that a chat log exists.
Trying Select or Expand does not help. Select returns nothing new,and expand gives an error along the lines of Message: Parsing OData Select and Expand failed: Property 'participants' on type 'microsoft.graph.onlineMeeting' is not a navigation property or complex property. Only navigation properties can be expanded., and similarly for chatinfo.
Also, using the threadId I thought maybe I could do this:
var groups = await graphClient.Groups.Request().GetAsync();
Group group = groups[0];
ConversationThread chat;
chat = await graphClient.Groups[group.Id].Threads[chatId].Request().GetAsync();
where for chatId I used the threadId from chatinfo, wholey and parsed out in different ways but I get Not Found.
No idea if what I'm trying to do is even possible as the documentation is rather lacking in terms of tying different pieces together (Like what is the threadId for? where is it used?).
Also, here are the various scopes I am requesting
"GraphScopes": "User.Read User.ReadBasic.All Mail.Send OnlineMeetings.ReadWrite Group.Read.All Team.ReadBasic.All"
We are trying to query shifts in the Microsoft Graph API using a C# app, now that StaffHub got deprecated , in the past we were getting an Unknown Error which looked like a permissions issue.
In the docs I noticed permissions for Schedule.ReadAll and Schedule.ReadWriteAll so I added them to the application permissions in our App Registration in Azure.
Now when we send the request to https://graph.microsoft.com/beta/teams/{teamid}/schedule we get this error:
Microsoft.Graph.ServiceException: 'Code: Forbidden Message: {"error":{"code":"Forbidden","message":"MS-APP-ACTS-AS header needs to be set for application context requests.","details":[],"innererror":{"code":"MissingUserIdHeaderInAppContext"}}}
The documentation says the Schedule permissions are in private preview, are these required for querying a schedule & shifts, and if so, is it possible to request access to the private preview?
I'm in the same situation. It's possible to request private preview access (we have), but I'm guessing that it's primarily granted to Microsoft partners or at least have a connection at Microsoft.
The workaround for me has been getting access on behalf of a user. It does however require the user to enter username and password in order to get an access token, so it might not be a perfect solution for you. But it works. You need to add (and, I believe, grant admin consent for) delegated permissions for this to work, either Group.Read.All or Group.ReadWrite.All.
Edit:
I've got it working now. We have private preview access, so I'm not sure this will help you unless you do too, but as I understand it will be available eventually. Given your question, I presume you already have an access token.
Add MS-APP-ACT-AS as a header with the user ID of the user you want the Graph client to act as.
If you're using the Graph SDK for .NET Core you can just add a header to the authentication provider:
public IAuthenticationProvider GetAuthenticationProviderForActingAsUser(string userId, string accessToken)
{
return new DelegateAuthenticationProvider(
requestMessage =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Get event times in the current time zone.
requestMessage.Headers.Add("Prefer", "outlook.timezone=\"" + TimeZoneInfo.Local.Id + "\"");
requestMessage.Headers.Add("MS-APP-ACTS-AS", userId);
return Task.CompletedTask;
});
}
Then you call the graph service client:
var authenticationProvider = GetAuthenticationProviderForActingAsUser(userId, accessToken);
var graphClient = new GraphServiceClient(authenticationProvider);
You should then be able to fetch the shifts:
var shifts = await graphClient.Teams[teamId].Schedule.Shifts
.Request()
.AddAsync(shift);
The documentation for the new google hangouts chat says that you need to authorize the scope https://www.googleapis.com/auth/chat.bot to do pretty much anything.
Here's the error:
While generating an authentication URL using their OAuth2 client I get the message that the scope is invalid. I don't have that problem if I use https://www.googleapis.com/auth/chat or some other scope like the one for google plus.
When I try to google things on in the API Explorer no combination of the URL or parts of the URL work either.
Here is my code to fetch the URL, seems to work just fine for everything else:
var {google} = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(
"clientid-idididid.apps.googleusercontent.com",
"_secretsuff",
"http://localhost:3000/auth/google/callback"
);
var scopes = [
"https://www.googleapis.com/auth/chat", //Works
"https://www.googleapis.com/auth/chat.bot" // Does not work
];
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: scopes,
});
console.log(url);
In case others are running across this problem I think I've figured this out. Google doesn't seem need this auth scope enabled by a domain user because it's already authorised on the domain when your testing your bot. The "authorisation" of these scopes are dictated by users in a domain adding/removing bots from spaces.
I'll go into a bit of detail if you're confused.
When you create a bot in the console for an organisation https://console.cloud.google.com/apis/api/chat.googleapis.com/ your bot is added to the domain and can be added to spaces by users. If then go over to to the credentials and create a service account you can use that json file credentials to access the API as your bot. The code below gets a list of the people in a space.
var { google } = require('googleapis');
var chat = google.chat("v1");
var key = require('./google_service-account-credentials.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/chat.bot'], // an array of auth scopes
null
);
jwtClient.authorize(function (err, tokens) {
chat.spaces.members.list({
auth: jwtClient,
parent: "spaces/AAAAD4xtKcE"
}, function (err, resp) {
console.log(resp.data);
});
});
If you try to get a list of members on other spaces (and other domains) the bot will fail with the exact same error message:
"Bot is not a member of the space."
I assume if you list your bot on the marketplace and it gets added to different domains and spaces google's API makes sure that your bot can do what it's trying to do on a space by space basis. It would be annoying have to setup some authentication flow after a bot has already been added for it to do its job. This is also probably why the current REST api doesn't let you list spaces under domains, it's not the paradigm this API works under.
It may have to do with one of the following:
The scope is created for service accounts. Make sure you are accessing the REST API with a service account.
Make sure that the bot is added to the room or space and has access to what you want it do.
Make sure the Service account is part of the bot project that you are using for the bot.
I'm trying to post to a Facebook page AS the page using the Unity Facebook SDK running on iOS. As I understand, to do that, I need the pages access token with manage_pages and publish_pages. I know that I can get it from /me/accounts?fields=access_token, but how do I tell AccessToken.CurrentAccessToken to use my pages access token instead?
Right now i'm using the following:
var wwwForm = new WWWForm();
//wwwForm.AddField ("access_token", "A-T I NEED");
wwwForm.AddBinaryData("image", screenshot, "InteractiveConsole.png");
wwwForm.AddField("message", "herp derp. I did a thing! Did I do this right?");
FB.API("/PAGE-ID/photos", HttpMethod.POST, HandleResult, wwwForm);
I tried putting the access token manually, but that didn't work (so I commented it out).
With this as it is I'm getting an error, telling me that I need publish_actions, wich is not correct since I'm not trying to post as the user. If I also get publish_actions the Post goes online, but is posted to the page as the user speaking. (User is also Admin)
Any Ideas ? Thanks!
So, I filed a bug report to facebook and as it turns out: "… at this time this functionality is not supported." Wich simply means there is now way to use the Page Access Token you acquired via the FB.API within the FB.API. And they are not going to tell you abot it in the documentation.
As a workaround I simply use a UnityWebRequest like this:
IEnumerator UploadToPage(byte[] screenshot) {
var wwwForm = new WWWForm();
wwwForm.AddField("message", "herp derp. I did a thing! Did I do this right?");
wwwForm.AddBinaryData("image", screenshot, "Test.png");
string url = "https" + "://graph.facebook.com/"+ PageID + "/photos";
url += "?access_token=" + PageAccessToken;
using (UnityWebRequest www = UnityWebRequest.Post(url, wwwForm))
{
yield return www.Send();
if (www.isError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
}
}
Debug.Log(url);
}
I'm trying to get the followers for a user that has authenticated through my meteorjs app. I have used the {{loginButtons}} template and have found where the users tokens are. However I now have to create my authorized request by hand and I was hoping this'd be easy. But it's really hard and I feel like I'm wasting time with trying to figure out a way to create the oauth_signature..
Any help is welcome!
Supposing it is Twitter you're talking about I might be able to help you out.
I just managed to do the same thing as you want to do.
This nice piece of code provides a client to the Twitter API: https://github.com/mynetx/codebird-js
Personally I have placed it in the server-folder in my app to avoid exposure of keys etc.
As the codebird-js code take use of XMLHttpRequests and node.js do not come with such functionality by default - at least in a meteor.js context - you have to add the XHR-functionality yourself.
This NPM did it for me: https://npmjs.org/package/xmlhttprequest
However, as you can not deploy your meteor app with additional npm packages I found this solution How can I deploy node modules in a Meteor app on meteor.com? that suggests placing it in the public folder.
Finally I added those lines of code in the codebird-js just below the line that says
var Codebird = function () {
var require = __meteor_bootstrap__.require;
var path = require('path');
var fs = require('fs');
var base = path.resolve('.');
var isBundle = fs.existsSync(base + '/bundle');
var modulePath = base + (isBundle ? '/bundle/static' : '/public') + '/node_modules';
var XMLHttpRequest = require(modulePath + '/xmlhttprequest').XMLHttpRequest;
Finally you have to provide your tokens generated at dev.twitter.com and find your user's tokens stored in the Users collection.
EDIT:
Whenenver you have the above you make a new Codebird object: var bird = new Codebird();
Then you set tokens:
bird.setToken(USER_ACCESS_TOKEN, USER_ACCESS_TOKEN_SECRET);
And makes the call:
bird.__call('friends/ids', {
screen_name': SCREEN_NAME,
user_id: TWITTER_ID
},
function(reply){
console.log(reply);
});
Note that USER_ACCESS_TOKEN, USER_ACCESS_TOKEN_SECRET, USER_NAME & TWITTER_ID in the above example are placeholders. They are all found in the Meteor Users collection.