C# Slack Bot to Slack Channel - using Slash command and not chat - c#

I'd like to send a slash command to a channel of my choice.
I'm using the Newtonsoft Json.NET serializer from NuGet. Currently, I have the following code:
string messageToSend = #"/Kyber test task here";
string channelToSendTo = "#general";
var urlWithAccessToken
= "https://hooks.slack.com/services/TOKEN/SPECIFIC/STUFF";
var client = new SlackClient(urlWithAccessToken);
client.PostMessage(username: "MyBot",
text: messageToSend,
channel: channelToSendTo);
And the SlackClient.cs
public SlackClient(string urlWithAccessToken)
{
_uri = new Uri(urlWithAccessToken);
}
public void PostMessage(string text, string username = null, string channel = null)
{
Payload payload = new Payload()
{
Channel = channel,
Username = username,
Text = text
};
PostMessage(payload);
}
public class Payload
{
[JsonProperty("channel")]
public string Channel { get; set; }
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("text")]
public string Text { get; set; }
}
It currently just says "/kyber test task here" instead of calling the /kyber slash command.
I've seen the undocumented chat.command command, but it doesn't appear to work with the Slack API now. I was hoping there was another way to do it without using the Legacy App functionality, since it surprises me that I was unable to find an example newer than about 3-4 years ago that didn't use it.

I guess this is the answer, but I asked Slack support about it. They replied:
It sounds like you might be asking about allowing an app to send a message containing the slash command, in order to trigger the slash command.
If so, I'm afraid that's not possible. Instead, since all slash commands essentially trigger a specific API endpoint, you'll need to contact the developers of that slash command to see if they make that endpoint publicly accessible so that you can create your own app mechanism to try and trigger commands to those endpoints.
I'm sorry that I don't have better news for you on this. I hope that info might help you to move forward though. Thanks for checking in on this.
So I think the answer is that the App I'm using doesn't support that functionality, and Slack doesn't/won't either. Thanks anyways for the help.

Related

How to call google.apis.dialogflow.v2 in C#

I am new to Google APIs. I want to know how to call Google Dialogflow API in C# to get intent form the input text. But I can't find any example to call Dialogflow using C#.
Please provide some example to call Dialogflow from C#.
If I understand your question correctly you want to call the DialogFlow API from within a C# application (rather than writing fulfillment endpoint(s) that are called from DialogFlow. If that's the case here's a sample for making that call:
using Google.Cloud.Dialogflow.V2;
...
...
var query = new QueryInput
{
Text = new TextInput
{
Text = "Something you want to ask a DF agent",
LanguageCode = "en-us"
}
};
var sessionId = "SomeUniqueId";
var agent = "MyAgentName";
var creds = GoogleCredential.FromJson("{ json google credentials file)");
var channel = new Grpc.Core.Channel(SessionsClient.DefaultEndpoint.Host,
creds.ToChannelCredentials());
var client = SessionsClient.Create(channel);
var dialogFlow = client.DetectIntent(
new SessionName(agent, sessionId),
query
);
channel.ShutdownAsync();
In an earlier version of the DialogFlowAPI I was running into file locking issues when trying to re-deploy a web api project which the channel.ShutDownAsync() seemed to solve. I think this has been fixed in a recent release.
This is the simplest version of a DF request I've used. There is a more complicated version that passes in an input context in this post:
Making DialogFlow v2 DetectIntent Calls w/ C# (including input context)
(Nitpicking: I assume you know DialogFlow will call your code as specified/registered in the action at DialogFlow? So your code can only respond to DialogFlow, and not call it.)
Short answer/redirect:
Don't use Google.Apis.Dialogflow.v2 (with GoogleCloudDialogflowV2WebhookRequest and GoogleCloudDialogflowV2WebhookResponse) but use Google.Cloud.Dialogflow.v2 (with WebhookRequest and WebhookResponse) - see this eTag-error. I will also mention some other alternatives underneath.
Google.Cloud.Dialogflow.v2
Using Google.Cloud.Dialogflow.v2 NuGet (Edit: FWIW: this code was written for the beta-preview):
[HttpPost]
public dynamic PostWithCloudResponse([FromBody] WebhookRequest dialogflowRequest)
{
var intentName = dialogflowRequest.QueryResult.Intent.DisplayName;
var actualQuestion = dialogflowRequest.QueryResult.QueryText;
var testAnswer = $"Dialogflow Request for intent '{intentName}' and question '{actualQuestion}'";
var dialogflowResponse = new WebhookResponse
{
FulfillmentText = testAnswer,
FulfillmentMessages =
{ new Intent.Types.Message
{ SimpleResponses = new Intent.Types.Message.Types.SimpleResponses
{ SimpleResponses_ =
{ new Intent.Types.Message.Types.SimpleResponse
{
DisplayText = testAnswer,
TextToSpeech = testAnswer,
//Ssml = $"<speak>{testAnswer}</speak>"
}
}
}
}
}
};
var jsonResponse = dialogflowResponse.ToString();
return new ContentResult { Content = jsonResponse, ContentType = "application/json" }; ;
}
Edit: It turns out that the model binding may not bind all properties from the 'ProtoBuf-json' correctly (e.g. WebhookRequest.outputContexts[N].parameters),
so one should probably use the Google.Protobuf.JsonParser (e.g. see this documentation).
This parser may trip over unknown fields, so one probably also wants to ignore that. So now I use this code (I may one day make the generic method more generic and thus useful, by making HttpContext.Request.InputStream a parameter):
public ActionResult PostWithCloudResponse()
{
var dialogflowRequest = ParseProtobufRequest<WebhookRequest>();
...
var jsonResponse = dialogflowResponse.ToString();
return new ContentResult { Content = jsonResponse, ContentType = "application/json" }; ;
}
private T ParseProtobufRequest<T>() where T : Google.Protobuf.IMessage, new()
{
// parse ProtoBuf (not 'normal' json) with unknown fields, else it may not bind ProtoBuf correctly
// https://github.com/googleapis/google-cloud-dotnet/issues/2425 "ask the Protobuf code to parse the result"
string requestBody;
using (var reader = new StreamReader(HttpContext.Request.InputStream))
{
requestBody = reader.ReadToEnd();
}
var parser = new Google.Protobuf.JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true));
var typedRequest = parser.Parse<T>(requestBody);
return typedRequest;
}
BTW: This 'ProtoBuf-json' is also the reason to use WebhookResponse.ToString() which in turn uses Google.Protobuf.JsonFormatter.ToDiagnosticString.
Microsoft's BotBuilder
Microsoft's BotBuilder packages and Visual Studio template.
I havent't used it yet, but expect approximately the same code?
Hand written proprietary code
A simple example of incoming request code (called an NLU-Response by Google) is provided by Madoka Chiyoda (Chomado) at Github. The incoming call is simply parsed to her DialogFlowResponseModel:
public static async Task<HttpResponseMessage> Run([...]HttpRequestMessage req, [...]CloudBlockBlob mp3Out, TraceWriter log)
...
var data = await req.Content.ReadAsAsync<Models.DialogFlowResponseModel>();
Gactions
If you plan to work without DialogFlow later on, please note that the interface for Gactions differs significantly from the interface with DialogFlow.
The json-parameters and return-values have some overlap, but nothing gaining you any programming time (probably loosing some time by starting 'over').
However, starting with DialogFlow may gain you some quick dialog-experience (e.g. question & answer design/prototyping).
And the DialogFlow-API does have a NuGet package, where the Gactions-interface does not have a NuGet-package just yet.

Entity Framework connection string from .DSN file

I have a problem, so I thought I would come to the brightest minds on the web.
I have written an ASP.NET MVC application that interfaces with a web service provided by another application. My app basically just adds some features to the other web application.
Both applications have a database. I am trying to limit the configuration for my application by using the other applications SQL Server credentials. This is so that if they decide to change the password for the other application, mine will just start working.
These credentials are saved in a .DSN file that my application can reach. How can I get my application, which uses Entity Framework, to use a connection string that is created from the details read in the .DSN file?
I can figure out the code to read the .DSN file, so if you wish to provide some code examples you can base them around setting the connection string for EF.
I am also open to other solutions, or even reasons why I shouldn't do this.
Thanks in advance.
PS. As I was writing this, I came up with a little concept. I am going to test it out now to see how it goes. But here is the basics:
On start up, read the needed details into static properties.
public MyContext() : base(getConnectionString()) { }
3.
private SomeObjectTypeHere getConnectionString()
{
//read static properties
//return .....something..... not sure yet....
}
Thoughts on that maybe?
EDIT
I have created a method that reads the .DSN file and gets the server, the user id and the password. I now have these stored in static properties. In my context, how can I set my connection string now that i have the required details.
So, the biggest issue that I was really having was how to set my connection string in Entity Framework. But I was also hoping that maybe someone else had worked with .DSN files.
Anyway, here was my solution. Still looking for problems that might arise from this, so if you can see any issues, let me know!
First, I created a method that was run on startup. This method ran through the .DSN file and picked out the gems.
Keep in mind that I have never worked with .DSN files, and the section that gets the password is unique to my situation.
var DSNFileContents = File.ReadAllLines(WebConfigurationManager.AppSettings["AppPath"] + #"\App.DSN");//reads DSN into a string array
//get UID
string uid = DSNFileContents.Where(line => line.StartsWith("UID")).First().Substring(4);//get UID from array
//test if uid has quotes around it
if (uid[0] == '"' && uid[uid.Length - 1] == '"')
{
//if to starts with a quote AND ends with a quote, remove the quotes at both ends
uid = uid.Substring(1, uid.Length - 2);
}
//get server
string server = DSNFileContents.Where(line => line.StartsWith("SERVER")).First().Substring(7);//get the server from the array
//test if server has quotes around it
if (server[0] == '"' && server[server.Length - 1] == '"')
{
//if to starts with a quote AND ends with a quote, remove the quotes at both ends
server = server.Substring(1, server.Length - 2);
}
//THIS WON'T WORK 100% FOR ANYONE ELSE. WILL NEED TO BE ADAPTED
//test if PWD is encoded
string password = "";
if (DSNFileContents.Where(line => line.StartsWith("PWD")).First().StartsWith("PWD=/Crypto:"))
{
string secretkey = "<secret>";
string IV = "<alsoSecret>";
byte[] encoded = Convert.FromBase64String(DSNFileContents.Where(line => line.StartsWith("PWD")).First().Substring(12));
//THIS LINE IN PARTICULAR WILL NOT WORK AS DecodeSQLPassword is a private method I wrote to break the other applications encryption
password = DecodeSQLPassword(encoded, secretkey, IV);
}
else
{
//password was not encrypted
password = DSNFileContents.Where(line => line.StartsWith("PWD")).First().Substring(4);
}
//build connection string
SqlConnectionStringBuilder cString = new SqlConnectionStringBuilder();
cString.UserID = uid;
cString.Password = password;
cString.InitialCatalog = "mydatabase";
cString.DataSource = server;
cString.ConnectTimeout = 30;
//statProps is a static class that I have created to hold some variables that are used globally so that I don't have to I/O too much.
statProps.ConnectionString = cString.ConnectionString;
Now that I have the connection string saved, I just have my database Context use it as below,
public class myContext : DbContext
{
public myContext() : base(statProps.ConnectionString) { }
//all my DbSets e.g.
public DbSet<Person> Persons{ get; set; }
}
This is simple, yes, but I hoping that it can provide some information to anyone that was looking to do something similar but was not sure about how it should be handled.
Again, let me know if you like or dislike this solution and if you dislike it, what is your solution and why.
Thanks again!

Quickblox chat setting save_to_history to true in Xamarin C# SDK

I'm using Quickblox C# SDK. I want to send message to a specific dialog. It's not well documented in Xamarin specific documentation. I decided to visit REST API documentation. As I could learn from there
By using Chat 2.0, you are not automatically storing your messages. Also a dialog entity won't be created/updated without saving a message to history.
I can infer if I set save_to_history to 1, chat dialog will be automatically created and message will be stored in the backend. However I couldn't figure out how I should specify that in C# SDK, cause extraParam in this method signature
public void SendMessage(int userId, string body, string extraParams, string dialogId, string subject = null, Quickblox.Sdk.Modules.ChatXmppModule.Models.MessageType messageType = Quickblox.Sdk.Modules.ChatXmppModule.Models.MessageType.Chat)
is just a string. I've dug into disassembled code and after some investigation understood that internally this parameter is used as XML so I tried these two options
var extraParams = "<extraParams> " +
"<save_to_history>1</save_to_history> " +
"</extraParams>";
And Also
var extraParams = "<save_to_history>1</save_to_history> ";
But none of these worked.
Anybody has idea how I should specify the extraParam?
Regards
The issue was simply that I forgot to call connect before I was sending a message.
Here is the method to send a message
public async Task SendMessageAsync(IUser sender, IChatMessage message, string channelID, CancellationToken token)
{
await loginIfRequired(sender, token);
var jsonMessage = JsonConvert.SerializeObject(message);
var recipientID = await getQuickbloxUserId(message.RecipientID, token);
var extraParams = "<extraParams> " +
"<save_to_history>1</save_to_history> " +
"</extraParams>";
_quickblox.ChatXmppClient.SendMessage(recipientID, jsonMessage, extraParams, channelID);
}
Inside loginIfRequired I call
_quickblox.ChatXmppClient.Connect(_currentUserID.Value, password);
And everything worked fine and the dialog was created.
Hope this will help someone.

Cache player with GameSparks and Unity

I am new to gamesparks, but so far I have set up a login/register function, which works like it should, but... How do I ensure that the user don't have to login next time he or she opens the app?
I found this in which I read that I can just run this:
GameSparkssApi.isAuthenticated().
First off all, in all other tutorials it states that it should be: GameSparks.Api.xxxx. Even when trying this I do not find isAuthenticated() anywhere.
GameSparks.Api.isAuthenticated();
I am hoping that someone can cast some light on this.
You can use Device Authentication for this purpose. This method of auth sets an access token for the device you are on. This token is stored and the client and gotten in the request. these requests are structured like so:
new GameSparks.Api.Requests.DeviceAuthenticationRequest()
.SetDeviceId(deviceId)
.SetDeviceModel(deviceModel)
.SetDeviceName(deviceName)
.SetDeviceOS(deviceOS)
.SetDeviceType(deviceType)
.SetDisplayName(displayName)
.SetOperatingSystem(operatingSystem)
.SetSegments(segments)
.Send((response) => {
string authToken = response.AuthToken;
string displayName = response.DisplayName;
bool? newPlayer = response.NewPlayer;
GSData scriptData = response.ScriptData;
var switchSummary = response.SwitchSummary;
string userId = response.UserId;
});
You can find more on this method in our documentation: https://api.gamesparks.net/#deviceauthenticationrequest
Regards Patrick, GameSparks.

MonoTorrent magnet link download does not start

I strongly believe that MonoTorrent library can do this, but it is probably due to the lack of documentation that I haven't been able to get it working.
To start with, MonoTorrent seems to be able to successfully download original torrents by using the following code:
https://smuxi.im/wiki/monotorrent/Managing_Torrents
But due to the increase of Magnet Links popularity, I would like to get magnet links working as well. The "trick" of getting .torrent out of them (like using the ones that µTorrent generates) doesn't work for me either even when using the same code as above. It stays stuck like this, founding 1-3 peers per second but making no progress:
StackOverflow best question / answer at this topic was MonoTorrent - Magnet link to Torrent file but unfortunately the answer didn't even match MonoTorrent constructors which are the following:
public TorrentManager(Torrent torrent, string savePath, TorrentSettings settings);
public TorrentManager(MagnetLink magnetLink, string savePath, TorrentSettings settings, string torrentSave);
public TorrentManager(Torrent torrent, string savePath, TorrentSettings settings, string baseDirectory);
public TorrentManager(InfoHash infoHash, string savePath, TorrentSettings settings, string torrentSave, IList<RawTrackerTier> announces);
Finally I went to try some other code, apparently you need to need to either pass it a MagnetLink or InfoHash, so I gave it a go with InfoHash like the following:
ClientEngine engine;
TorrentManager manager;
string savePath;
public TorrentDownload(string savePath)
{
this.engine = new ClientEngine(new EngineSettings());
this.savePath = savePath;
}
public void DownloadMagnet(string hash)
{
manager = new TorrentManager(InfoHash.FromHex(hash), savePath, new TorrentSettings(), savePath, new List<RawTrackerTier>());
engine.Register(manager);
manager.Start();
}
Am I missing something that my download doesn't even start? No errors / no crashes

Categories